티스토리 뷰

sql문의 분류

DDL: Data Definition Language

  • Create문: 테이블 및 각종 객체를 새로 만든다.
  • Drop문: 테이블 및 각종 객체를 없앤다.
  • Alter문: 테이블 및 각종 객체의 구조를 바꾼다.

 

DML: Data Manipulation Language

  • Insert문: 새로운 레코드 삽입
  • Delete문: 레코드 삭제
  • Update문: 레코드 내의 특정값을 변경

 

DCL: Data Control Language

  • Commit: 트랜잭션 완료
  • Roll Back: 다시 돌아감

DQL: Data Query Language

  • Select
    • DQL을 따로 정의하지 않는 DML로 봄.

 

테이블 스키마는 속성의 집합으로 표현이 된다.


Create and Drop

자료형(Data type)

  • 테이블을 생성할 때 각 속성마다 형태와 크기를 정해주어야 한다.
  • 속성에 들어갈 값의 형태와 크기를 고려하여 자료형을 정한다.

 

많이 쓰는 자료형

  • char(n): fixed-length character string
  • varchar2(n): variable-length character string
  • int, smallint
  • numeric(p, d), number(p, d), decimal(p, d), dec(p, d)
  • real, double, float(p)
  • 오라클에는 int가 없어 int로 저장하면 알아서 num으로 변경해줌.

 


Create

Create table ~

  • 테이블 스키마를 생성
  • 그 외의 객체들도 생성할 때 Create를 사용한다.
  • 속성 이름이 자료형보다 먼저 옴.
  • foreign key 테이블 이름 뒤에 속성을 지정하지 않으면 그 테이블의 기본키를 참조하는 것이다.

 

수강 과목 학생

  • 생성시: 학생->수강
  • 삭제시: 수강->학생

 

 

 

구조

create table 테이블_이름 (
    속성_이름1 자료_형1,
    속성_이름2 자료_형2,
    ...,
    primary key (속성11, 속성12, ...),
    foreign key (속성21, 속성22, ...) references 테이블_이름2
);

-- 학생 테이블
create table 학생 (
    학번 varchar2(7),
    이름 varchar2(20),
    학과번호 varchar2(3),
    이수학점 int,
    평점 float,
    primary key (학번)
);

-- 과목 테이블
create table 과목 (
    과목번호 varchar2(5),
    과목이름 varchar2(30),
    최대인원 int,
    primary key (과목번호)
);

-- 수강 테이블
create table 수강 (
    학번 varchar2(7),
    과목번호 varchar2(5),
    primary key (학번, 과목번호),
    foreign key (학번) references 학생,
    foreign key (과목번호) references 과목
);

 

공통 속성의 자료형은 똑같아야 한다.

  • 학생 테이블의 학번이 varchar2(7)이면 수강 테이블의 학번도 varchar2(7)이어야 한다.

 


Drop

Drop table 테이블이름;

  • 테이블 내의 데이터를 포함, 스키마까지 모두 삭제
  • 그 외의 객체들도 삭제할 때 Drop을 사용한다.
  • Create는 여러 가지 문법 구조를 가지지만 Drop은 단순하다.

 


⚠️ 여러 개의 테이블을 만들거나 지울 때 주의할 점

  • 여러 개의 테이블을 만들 때 어떤 테이블에 외래 키가 있을 경우,
  • 예를 들어 테이블 A의 기본 키인 속성이 B에 있을 경우,
  • A를 먼저 만들어야 B에서 외래 키 지정을 할 수 있다.
  • 반대로 지울 때는 B에 있는 외래 키 지정 때문에 A는 바로 지워지지 않는다. 따라서 B부터 지워야 한다.

 


Insert

구조

Insert into 테이블_이름
(속성_이름1, 속성_이름2, ...,) values (값1, 값2, ...);
  • 속성 이름들을 지정하지 않으면 Create할 때 설정했던 속성 순서대로 값들만 지정
  • 속성 이름들을 지정했을 경우 지정되지 않은 속성에는 null이 들어간다.
  • 일부러 null을 집어넣을 수도 있다.
-- 학번이 '9966666', 이름이 '이순신', 이수학점이 0, 평점이 0.0인 레코드를 학생 테이블에 삽입(학과번호는 null)
Insert into 학생 values ('9966666', '이순신', null, 0, 0);
Insert into 학생 (이름, 학번, 평점, 이수학점) values ('이순신', '9966666', 0, 0);

 

NULL의 특성

  • 어떤 레코드는 삽입되었는데 그 중 특정 속성에 값이 들어있지 않다는 의미
  • null인 속성에 대한 비교 연산의 결과는 모두 false
  • null인지 아닌지 비교하는 구문 : Is null, Is not null
Select * from 학생 where 학과번호 = '010';
Select * from 학생 where 학과번호 != '010';
Select * from 학생 where 학과번호 is null;

 


Update

  • 이미 입력된 레코드의 빈 속성에 값을 넣거나 속성 하나의 값을 삭제하는 것은 Update이다.
  • 정확한 레코드를 where 절을 통해 선택
    • where 절 없으면 모든 레코드가 선택됨
  • =는 비교연산자가 아니라 대입연산자가 된다.
  • 행에 있는 값을 변경

 

구조

Update 테이블_이름 Set
 속성1 = 값1, 속성2 = 값2, ...
Where 조건1 (and/or) 조건2 (and/or) ...;

Update 학생 set 학과번호 = '030' where 학번 = '9966666';
Update 학생 set 이수학점 = 이수학점 + 20;

 


 

Delete

  • 조건이 없으면 모든 레코드를 삭제
  • Drop은 스키마도 없애지만 Delete는 레코드만 삭제
  • delete는 레코드 단위로 삭제하므로 delete와 from 사이에 아무것도 없다.
  • delete from 하고 where 절을 안쓰면, 스키마는 남아있지만 레코드는 다 삭제된 것
  • drop은 스키마까지 다 삭제함.

 

구조

Delete From 테이블_이름
Where 조건1 (and/or) 조건2 (and/or) ...;

Delete from 학생 where 학번 = '9966666';

 

✅ insert와 delete는 레코드 단위로 삽입, 삭제가 발생한다.


이미 있는 테이블을 이용하여 새 테이블 만들기

 

Create table 테이블_이름 as [sub-query];

Create table SW공학과학생 as
select * from 학생 where 학과번호 = '010';

 

sub-query에 의한 insert

Insert into 테이블 [(컬럼1, 컬럼2, 컬럼3...)]
[sub-query];

Insert into SW공학과학생 (학번, 이름, 학과번호)
Select 학번, 이름, 학과번호 from 학생
where 학과번호 = '020';
  • 일반적인 방법과는 달리 VALUES절을 사용하지 않는다.
  • Insert절에 명시된 컬럼 수가 sub-query에 사용된 컬럼 수와 일치해야 하며 데이터 타입도 일치해야 한다.
  • sub-query가 리턴하는 행 수만큼 입력된다.

 


합집합, 교집합, 차집합

 

Union(합집합)

  • 두 개의 select문이 결과 속성 집합이 같을 때, 두 결과 집합에 대해 합집합이 가능
  • 이 때 사용하는 키워드가 Union
  • Union all 키워드를 쓰면 중복되는 데이터를 중복되는 횟수만큼 보여준다.
select 학번, 이름 from 학생
where 학과번호 = '010'
union
select 학번, 이름 from 학생
where 이수학점 >= 100;

->

select 학번, 이름 from 학생
where 학과번호 = '010' or 이수학점 >= 100;

 


Intersect(교집합)

  • 두 개의 select문의 결과 속성 집합이 같을 때, 두 결과 집합에 대해 교집합이 가능
  • 이 때 사용하는 키워드가 Intersect
  • 자연 조인보다 intersect를 쓰는 것이 좋음.
select 학번, 이름 from 학생
where 학과번호 = '010'
intersect
select 학번, 이름 from 학생
where 이수학점 >= 100;

->

select 학번, 이름 from 학생
where 학과번호 = '010' and 이수학점 >= 100;

 


Minus(차집합)

  • 두 개의 select문의 결과 속성 집합이 같을 때, 두 결과 집합에 대해 차집합이 가능
  • 이 때 사용하는 키워드가 Minus
select 학번, 이름 from 학생
where 학과번호 = '010'
minus
select 학번, 이름 from 학생
where 이수학점 >= 100;

->

select 학번, 이름 from 학생
where 학과번호 = '010' and 이수학점 < 100;

 


합집합, 교집합, 차집합의 유용성

-- 학과번호가 '020'인 학생 중에 과목번호 'cs300'인 과목을 수강하는 학생의 학번을 찾으시오.

select 학생.학번 from 학생, 수강 
where 학생.학번 = 수강.학번 
and 수강.과목번호 = 'cs300' 
and 학생.학과번호 = '020';

select 학번 from 학생 where 학과번호 = '020';
intersect
select 학번 from 수강 where 과목번호 = 'cs300';

 


통계 함수

대표적인 통계 함수

  • sum: 그 속성의 합계를 구한다.
  • count: 투플의 수를 구한다.
  • avg:  그 속성의 평균값을 구한다.
  • min: 그 속성의 최솟값을 구한다.
  • max: 그 속성의 최대값을 구한다.

 

count

-- 학생들의 수강 신청한 내역의 개수를 보여주는 SQL문을 만드시오
select count(*) as 수강신청건수 from 수강;

-- 학번이 '9912345'인 학생이 몇 과목을 신청했는지 구하는 SQL문을 만드시오.
select count(*) as 수강신청건수 from 수강
where 학번 = '9912345';

 

sum, avg

-- 학과번호가 '010'인 학생들의 이수학점 합계를 구하는 SQL문을 만드시오.
select sum(이수학점) as 이수학점합계 from 학생 
where 학과번호 = '010';

-- 학과번호가 '010'인 학생들의 이수학점 평균을 구하는 SQL문을 만드시오.
select avg(이수학점) as 이수학점합계 from 학생 
where 학과번호 = '010';

 


통계 함수의 처리

Group by

group by 절이 없을 때

  • 그 속성에 대한 통계값을 구함

 

group by 절이 있을 때

  • group by 뒤에 나오는 속성으로 투플들을 나눔
  • 각 그룹별로 통계값을 구함
  • 그룹 기준이 되는 속성 이외의 속성을 표시할 수 없음
  • group by 앞에 where 절이 있으면 where 절 수행 후 group by
-- 학과번호별로 이수학점의 합계를 구하는 SQL문을 만드시오.
select 학과번호, sum(이수학점) as 이수학점합계
from 학생 group by 학과번호;

 

  • 일반적인 속성과 통계값은 같이 select 할 수 없지만, group 기준이 되는 속성은 같이 select 할 수 있다.
  • 아래 예는 오류 발생
-- 학과번호가 '010'가 아닌 학생들의 학번과 이수학점 합계를 구하는 SQL문을 만드시오.
select 학번, sum(이수학점) as 이수학점합계
from 학생 where 학과번호 != '010'
group by 학과번호;

 


Having

  • 통계 함수를 사용할 때 레코드를 선택하기 위한 조건이 아닌 그룹별 통계 조건을 지정하기 위한 SQL문
-- 학생들의 이수학점 평균이 100이상인 학과의 학과번호를 보여주는 SQL문을 만드시오.
select 학과번호 from 학생
group by 학과번호
having avg(이수학점)>=100;

 


 

단순한 Select문이 잘 안 되는 경우

-- 수강 테이블에서 'CS100'과 'CS300'을 모두 듣는 학생들의 학번을 구하시오.
select 학번 from 수강
where 과목번호 = 'cs100' and 과목번호 = 'cs300'; (X)

-- 학생 테이블에서 학과번호가 '020'인 학생 모두보다 이수학점이 많은 학생들의 학번을 구하시오.
select 학번 from 학생 where 이수학점 > 120; (X)

-- 학생 테이블에서 학과번호가 '020'인 학생들 중 어느 한 사람보다 이수학점이 많은 학생들의 학번을 구하시오.
select 학번 from 학생 where 이수학점 > 90; (X)

 

해결방법

  • 투플 변수 이용하기
    • 투플 변수를 이용하여 하나의 테이블을 마치 두 개의 테이블이 있는 것처럼 두고 카티전 곱을 한 후 필요한 레코드와 속성들을 뽑아낸다.
  • 중첩 SQL문 이용하기
    • 중첩 SQL문을 이용하여 원하는 값들의 집합을 만들어 그 값들과의 비교를 통해 원하는 레코드와 속성들을 뽑아낸다.

투플 변수를 이용한 Select 문

투플 변수

  • SQL문을 실행하는 도중에 투플 하나를 그 때 그 때 담아두기 위한 변수를 투플 변수라고 한다.

 

사용방법

  1. 테이블 이름 뒤에 투플 변수 이름을 붙인다.
  2. 투플 변수의 형태는 테이블 스키마와 같다.
select t1.학번, t1.이름, t2.과목번호
from 학생 t1, 수강 t2
where t1.학번 = t2.학번;
  • 즉, 평소에는 테이블 이름이 투플 변수 이름이 된다.

 

-- 학생 테이블에서 학번이 '9912345'인 학생보다 이수학점이 많은 학생들의 학번과 이름을 검색하시오.
select s1.학번, s1.이름
from 학생 s1, 학생 s2
where s2.학번 = '9912345'
and s1.이수학점 > s2.이수학점;

 


중첩 Select문

  • 하나의 SQL문에 Select문을 두 번 이상 사용하는 것을 중첩 Select문이라고 한다.

 

사용방법

  1. 필요한 값들의 집합을 Select문을 이용해서 구한다.
  2. 이 집합을 조건 식을 만들 때 in, =, some, all 등의 키워드와 같이 사용한다.
select 학번
from 수강
where 과목번호 = 'CS100' and
학번 in
(select 학번
 from 수강
 where 과목번호 = 'CS300');
 
select 학번
from 학생
where 이수학점 > some
(select 이수학점
 from 학생
 where 학과번호 = '020');
  
select 학번
from 학생
where 이수학점 > All
(select 이수학점
 from 학생
 where 학과번호 = '020');

 

과목번호 cs100과 cs300을 모두 듣는 학생의 학번을 구해 보자.

-- intersect 사용 가능
select 학번 from 수강 where 과목번호 = 'cs100'
intersect 
select 학번 from 수강 where 과목번호 = 'cs300';

-- 튜플 변수 사용
select s1.학번 from 수강 s1, 수강 s2
where s1.학번 = s2.학번
and s1.과목번호 = 'cs100' and s2.과목번호 = 'cs300';

-- 학번 값 한개가 어떤 집합에 있는가
select 학번 from 수강 where 과목번호 = 'cs100' and
학번 in (select 학번 from 수강 where 과목번호 = 'cs300');

 

데이터는 변할 수 있으므로 일반화해서 구해야 함.

select 학번 from 학생 where 이수학점 > 120;

-- 방법1) all은 안에 있는거보다 다 커야하고, some은 일부만 커도 됨.
select 학번
from 학생
where 이수학점 > some
(select 이수학점
from 학생
where 학과번호 = '020');

select 학번
from 학생
where 이수학점 > all
(select 이수학점
from 학생
where 학과번호 = '020');

-- 특정 한 학생의 학점보다 큰거는 튜플 변수도 이용 가능

문제

-- 수강신청 데이터베이스에서 학생 테이블에서 전체 학생의 평점 평균을 구해 보자.
select avg(평점) as 평점평균
from 학생;

-- 학생 테이블에서 학과번호별 학생들의 평점 평균을 구해 보자.
select 학과번호, avg(평점) as 평점평균
from 학생
group by 학과번호;

-- 학생 테이블에서 이수학점이 120 이상인 학생 수를 구해 보자.
select count(*) as 마지막학기학생수
from 학생
where 이수학점 >= 120;

-- 수강 테이블에서 각각의 학생이 몇 과목을 수강신청했는지 구해 보자.
select count(*) as 신청한과목수
from 수강
group by 학번;

-- 수강 테이블에서 신청과목 수가 2 이상인 학생들이 몇 과목을 수강신청했는지 구해 보자.
select 학번, count(*) as 신청한과목수
from 수강
group by 학번
having count(*) >= 2;

-- 수강 테이블에서 학생들이 몇 과목을 수강신청했는지 구해 보자.(단 학생 이름이 나오게 하자)
select 학생.이름, count(*) as 신청한과목수
from 학생, 수강
where 학생.학번 = 수강.학번
group by 학생.이름;

-- 수강 테이블에서 학번이 '9912345'인 학생과 학번이 '9955555'인 학생이 모두 수강신청한 과목의 과목번호를 구해 보자.(투플 변수를 이용)
select s1.과목번호
from 수강 s1, 수강 s2
where s1.학번 = '9912345' and s2.학번 = '9955555'
and s1.과목번호 = s2.과목번호;

-- 학생 테이블에서 학번이 ' 9933333 '인 학생보다 평점이 높은 학생의 학번과 이름을 구해 보자.(중첩 쿼리를 이용)
select 학번, 이름
from 학생
where 평점 > (select 학점 from 학생
 where 학번 = '9933333');

 


실습 문제

-- 아래의 설명에 해당하는 테이블을 만들어 보자
테이블 이름 : 서울회원연락처
속성들 : {회원번호, 성명, 주소, 전화번호}, PK : {회원번호}
포함될 레코드 : 회원 테이블에 있는 회원들 중 서울에 사는 회원들의 정보 
속성들의 자료 형은 회원 테이블에 있는 동명의 속성들과 같다.
이 테이블은 이미 있는 테이블을 이용하지 말고 Create문과 Insert문을 이용하여 만들자
create table 서울회원연락처 (
  회원번호 varchar2(7),
  성명 varchar2(20),
 주소 varchar2(50),
  전화번호 varchar2(20),
  primary key (회원번호)
);
insert into 서울회원연락처 values ('T007', '김한수', '서울시 용산구' , '(019)1234-0001');
insert into 서울회원연락처 values ('T009', '이신애', '서울시 송파구', '(011)1234-0003');
insert into 서울회원연락처 values ('T010', '권동하', '서울시 관악구', '(016)1234-0004'); 


-- 아래의 설명에 해당하는 테이블을 만들어 보자
테이블 이름 : 지방회원연락처
속성들 : {회원번호, 성명, 주소, 전화번호}
포함될 레코드 : 회원 테이블에 있는 회원들 중 서울에 살지 않는 회원들의 정보 
속성들의 자료 형은 회원 테이블에 있는 동명의 속성들과 같다.
이 테이블은 이미 있는 회원 테이블을 이용하여 만들자.(Sub query를 이용한 Create문)
create table 지방회원연락처 as
select 회원번호, 성명, 주소, 전화번호 from 회원 where not 주소 like '%서울%';

-- 우리 쇼핑몰에서는 아직 구체적인 상품의 종류는 결정되지 않았으나 앞으로 키보드(분류코드는 4)들도 판매할 계획이다. 이 상황을 SQL로 표현해 보자.
insert into 분류 values (4, '키보드');

-- 우리 쇼핑몰에서는 거래처번호가 4인 거래처로부터 새로운 물건(상품번호가 78이고 상품명은 “삼성 32GB USB 메모리”, 분류코드는 2)을 판매할 것이다.  아직 단가는 결정되지 않았다. 이 상황을 SQL로 표현해 보자.
insert into 상품 values (78, '삼성 32GB USB 메모리', 4, null, 2);

-- 회원들 중 회원번호가 ‘T001’인 회원이 ‘부산시 금정구‘로 이사를 하였다. 이 상황을 SQL로 표현해 보자.
update 회원 set 주소 = '부산시 금정구' where 회원번호 = 'T001'; 

-- 우리 쇼핑몰에서 판매하는 물건들 중 마우스들의 단가가 10% 인상되었다. 이 상황을 SQL로 표현해 보자.
update 상품 set 단가 = 단가 * 1.1 where 상품명 like '%마우스%';

-- 지금까지 판매하던 물건들 중 상품번호가 8인 상품은 앞으로 판매하지 않을 것이다. 이 상황을 SQL로 표현해 보자.
delete from 주문상세내역 where 상품번호 = 8;
delete from 상품 where 상품번호 = 8;

-- 회원번호가 ‘T004’인 회원이 탈퇴하였다. 이 상황을 SQL로 표현해 보자. 1, 2, 23
회원, 주문, 주문상세내역
delete from 주문상세내역 where 주문번호 in (select 주문번호 from 주문 where 회원번호='T004');
delete from 주문 where 회원번호 = 'T004';
delete from 회원 where 회원번호 = 'T004';

-- 회원들 중 여성이면서 한 번이라도 주문을 한 적이 있는 회원들의 회원번호를 찾는 SQL문을 만들어 보자.
select 회원번호 from 회원 where 성별='여' and 회원번호 in (select 회원번호 from 주문);
select 회원번호 from 회원 where 성별='여'
intersect
select 회원번호 from 주문;

-- 상품들 중 단가가 10000원 이하면서 한 번도 판매되지 않은 상품들의 상품번호를 찾는 SQL문을 만들어 보자.   
select 상품번호 from 상품 where 단가<=10000 and 상품번호 not in (select 상품번호 from 주문상세내역);
select 상품번호 from 상품
minus
select 상품번호 from 주문상세내역
intersect
select 상품번호 from 상품 where 단가<=10000;

-- 상품 테이블에서 전체 상품의 단가 평균을 구해 보자.
select avg(단가) as 전체상품단가평균 from 상품;

-- 상품 테이블에서 분류코드에 따른 상품들의 단가 평균을 구해 보자.
select 분류코드, avg(단가) as 분류코드단가평균 from 상품 group by 분류코드;

-- 상품 테이블에서 단가가 10000원 이상인 상품의 개수를 구해 보자.
select count(*) as 만원이상상품개수 from 상품 where 단가>=10000;

-- 주문 테이블에서 회원번호에 따른 주문 횟수를 구해 보자. 
select 회원번호, count(*) as 주문횟수 from 주문 group by 회원번호;

-- 주문 테이블에서 세 번 이상 주문한 회원의 회원번호를 구해 보자.
select 회원번호 from 주문 group by 회원번호 having count(*)>=3;

-- 상품, 주문상세내역 테이블에서 상품번호에 따른 주문액 합계를 구해 보자.
select 주문상세내역.상품번호, sum(상품.단가*주문상세내역.수량) as 주문액합계
from 상품, 주문상세내역
where 상품.상품번호=주문상세내역.상품번호
group by 주문상세내역.상품번호;

-- 상품, 주문상세내역 테이블에서 상품번호에 따른 주문액 합계가 100,000원 이상인 상품의 상품번호를 구해 보자.
select 주문상세내역.상품번호, sum(상품.단가*주문상세내역.수량) as 주문액합계
from 상품, 주문상세내역
where 상품.상품번호=주문상세내역.상품번호
group by 주문상세내역.상품번호
having sum(상품.단가*주문상세내역.수량)>=100000;

-- 주문, 주문상세내역, 상품 테이블에서 주문번호에 따른 주문액 합계를 구해 보자.
select 주문상세내역.주문번호, sum(상품.단가*주문상세내역.수량) as 주문액합계
from 주문, 주문상세내역, 상품
where 주문.주문번호=주문상세내역.주문번호
and 상품.상품번호=주문상세내역.상품번호
group by 주문상세내역.주문번호;

-- 주문 테이블에서 2017년 1월 2일과 1월 3일에 모두 주문한 회원의 회원번호를 구하시오.
   -- intersect를 이용하여 구하시오.
select 회원번호 from 주문 where 주문일='2017-01-02'
intersect
select 회원번호 from 주문 where 주문일='2017-01-03';
   -- 중첩 쿼리문을 이용하여 구하시오.
select 회원번호 from 주문
where 주문일='2017-01-02' 
and 회원번호 in (select 회원번호 from 주문 where 주문일='2017-01-03');

-- 상품 테이블에서 분류코드가 1인 상품들의 모든 단가보다 단가가 큰 상품의 상품번호와 상품명을 구하시오.
   -- 중첩 쿼리문을 이용하여 구하시오.
select 상품번호, 상품명 from 상품
where 단가 > all (select 단가 from 상품 where 분류코드=1);

-- 상품 테이블에서 상품번호가 14인 상품의 단가보다 단가가 큰 상품의 상품번호와 상품명을 구하시오.
   -- 투플 변수를 이용하여 구하시오.
select P1.상품번호, P1.상품명 
from 상품 P1, 상품 P2
where P2.상품번호=14
and P1.단가>P2.단가;