티스토리 뷰

SQL

SQL 뽀개기 - 8 | 서브쿼리

세댕댕이 2021. 7. 30. 16:56

서브쿼리 : 쿼리문 안에 또다른 쿼리문이 포함된 구문

 

단일행 서브쿼리 : 하나의 값만 리턴한다. 스칼라값 하나를 리턴

다중행 서브쿼리 : 여러개의 행을 리턴한다.

다중행 열 서브쿼리 : 여러개의 열로 구성된 여러개의 행인 테이블을 리턴한다

 

1. 단일행 서브쿼리

SELECT MAX(popu), name FROM tCity -- ERROR
SELECT name FROM tCity WHERE popu = MAX(popu); -- ERROR

SELECT name FROM tCity WHERE popu = (SELECT MAX(popu) FROM tCity); -- CORRECT
SELECT item FROM tItem WHERE num = (SELECT MAX(num) FROM tItem); -- CORRECT
SELECT name FROM tStaff WHERE score = (SELECT MAX(score) FROM tStaff);
SELECT name FROM tStaff WHERE salary >= (SELECT AVG(salary) FROM tStaff);
SELECT member, addr FROM tMember WHERE age = (SELECT MAX(age) FROM tMember);

-> 서브쿼리를 감싸는 전체 쿼리를 외부쿼리라고 부른다.

-> 구분을 위해서 서브쿼리를 괄호로 감싼다

-> 서브쿼리를 먼저 실행하고, 그 이후에 외부쿼리를 실행하는 순차적 방식.

- 단일행 서브쿼리는 하나의 결과만 리턴하며, 주로 WHERE, HAVING 등의 조건절에 쓰인다.

-- 1. 따로따로 생각해 놨다가
SELECT category FROM tItem WHERE item = '청바지';
SELECT delivery FROM tCategory WHERE category = '패션';

-- 2. 두개를 합쳐서 표현
SELECT delivery FROM tCategory WHERE category = (SELECT category FROM tItem WHERE item = '청바지');

- 서브쿼리의 중첩 횟수에는 제약이 없다.

SELECT item FROM tItem WHERE price = 70000;

SELECT member FROM tOrder WHERE item = 
(SELECT item FROM tItem WHERE price = 70000);

SELECT age FROM tMember where member = 
(SELECT member FROM tOrder WHERE item = 
(SELECT item FROM tItem WHERE price = 70000));

- 단일행 서브쿼리는 단일값을 리턴함으로 =, <, > 와 같은 연산자와 같이 사용이 가능하다.

 

2. 다중행 서브쿼리

- 다중행 서브쿼리는 여러개의 결과를 리턴하므로 연산자(=, >, < 등..) 와 같이 사용할 수 없다.

- 대신 IN 연산자를 사용

SELECT item, price FROM tItem WHERE item IN
(SELECT item FROM tOrder WHERE member = '향단');

 

3. 다중열 서브쿼리

SELECT * FROM tStaff WHERE (depart, gender) =
(SELECT depart, gender FROM tStaff WHERE name = '윤봉길');

SELECT * FROM tStaff WHERE (depart, salary) IN
(SELECT depart, MAX(salary) FROM tStaff GROUP BY depart);

UPDATE tCity SET (area, popu) = (SELECT area, popu FROM tCity WHERE name = '부산')
WHERE name = '서울';

결과셋이 다중행인 경우 IN 연산자로 비교해야 한다

 

- 일괄 비교가 성립하기 위해서는 비교 대상과 서브쿼리의 컬럼 개수가 같아야 한다!!

 

# 서브쿼리 연산자

IN 연산자는 괄호 안의 결과셋과 순차적으로 비교한다

ANY, ALL 연산자는 결과셋 전체와 비교한다

ANY : 결과셋 중 하나라도 만족하는 값이 있으면 참

ALL :결과셋 모두가 만족해야 참

SELECT name FROM tStaff WHERE salary > ANY
(SELECT salary FROM tStaff WHERE depart = '영업부');
-- ANY | 영업부 "최소" 월급보다 많은 사람  

SELECT name FROM tStaff WHERE salary > ALL
(SELECT salary FROM tStaff WHERE depart = '영업부');
-- ALL | 영업부 "최대" 월급보다 많은 사람

SELECT item, price FROM tItem WHERE item = ANY
(SELECT item FROM tOrder WHERE member = '향단');

= ANY ~~ 는 그중 하나라도 같은 것을 찾는다. 결과적으로는 IN 연산자와 동일한 역할.

 

# 연관 서브쿼리 (Correlated Subquery)

- 위의 서브쿼리들은 모두 단독으로 실행가능한 독립 서브쿼리였다.

- 연관 서브쿼리는 외부 쿼리의 필드값을 참조하며 단독으로 실행할 수 없다.

SELECT member, item, (SELECT price FROM tItem WHERE tItem.item = tOrder.item) price
FROM tOrder;

SELECT item, (SELECT price FROM tCategory WHERE tCategory.category = tItem.category) price
FROM tItem;

# EXISTS

- 결과셋이 있는지 조사하는 연산자

- 단독 실행은 불가능.. IF문이나 WHERE절과 같이 사용한다

SELECT * FROM tMember M WHERE EXISTS
(SELECT * FROM tOrder O WHERE O.member = M.member);

SELECT * FROM tMember M WHERE NOT EXISTS
(SELECT * FROM tOrder O WHERE O.member = M.member);

- IN 연산자로도 구할 수 있으나, EXISTS 연산자는 존재 유무만 체크하므로 속도가 더욱 빠르다

 

* 인라인 뷰

서브쿼리는 주로 WHERE절, SELECT절의 필드목록, FROM절에서 사용된다.

여기서 FROM절에 오는 서브쿼리는 하나의 값이 아닌 테이블을 리턴한다. 

-> FROM절의 서브쿼리를 특별히 인라인 뷰(inline view)라고 정의한다.

- 인라인이 붙은 이유는 CREATE로 영구저장 하지않고 잠시 만들어쓰고 버리는 뷰라는 뜻

 

SELECT * FROM (SELECT * FROM tCity) [별명];

SELECT * FROM (SELECT name, popu, area FROM tCity) A;
SELECT * FROM (SELECT * FROM tCity WHERE metro = 'y') B;

SELECT member, addr FROM (SELECT * FROM tMember WHERE age < 19) A
WHERE A.money > 100000;

SELECT * FROM (SELECT * FROM tStaff WHERE grade = '과장' OR grade = '부장') A
WHERE A.score >= 70;

인라인뷰는 영구적으로 저장되는 객체가 아니기 때문에 쿼리 실행중 이 테이블을 가리키기 위해서는 별명이 있어야한다

 

SELECT * FROM (
    SELECT name, popu * 10000 AS ingu FROM tCity
) A
WHERE A.ingu > 1000000;

SELECT * FROM (
    SELECT name, popu * 10000 AS ingu FROM tCity
) A
WHERE ingu > 1000000;


-- 1중첩 인라인
SELECT name, (popu * 10000 / area) AS dens
    ,CASE
        WHEN (popu * 10000 / area) > 1000 THEN '고밀도'
        WHEN (popu * 10000 / area) > 100 THEN '중밀도'
        ELSE '저밀도'
    END densgrade
FROM tCity;


-- 2중첩 인라인
SELECT name, dens
    ,CASE
        WHEN dens > 1000 THEN '고밀도'
        WHEN dens > 100 THEN '중밀도'
        ELSE '저밀도'
    END densgrade
FROM (
    SELECT name, (popu * 10000 / area) AS dens FROM tCity
) CD;


-- 3중첩 인라인
SELECT name, dens, densgrade
    ,CASE
        WHEN densgrade = '고밀도' then '8차로'
        WHEN densgrade = '중밀도' then '4차로'
        ELSE '2차로'
    END roadplan
FROM (
    SELECT name, dens
        ,CASE
            WHEN dens > 1000 THEN '고밀도'
            WHEN dens > 100 THEN '중밀도'
            ELSE '저밀도'
        END densgrade
    FROM (
        SELECT name, (popu * 10000 / area) AS dens FROM tCity
    )
);

-> 여러번 사용할 계산식을 인라인뷰에서 필드로 정의하고, 그 바깥에서는 필드를 참조하는 방식.

 

# 테이블 연산자

1. UNION

복수개의 결과셋에 대한 합집합을 생성한다.

기본적으로 두 집합 사이 공통적으로 존재하는 메소드가 있다면 하나로 합친다(중복X)

--> UNION ALL 을 사용시 중복을 제거하지 않고 모두 출력한다

SELECT * FROM tItem WHERE category = '식품'
UNION
SELECT * FROM tItem WHERE category = '가전';

SELECT DISTINCT depart FROM tStaff WHERE salary > 400
UNION ALL
SELECT DISTINCT depart FROM tStaff WHERE score > 80;

하나의 테이블에서 조건을 만드려 한다면 OR 연산자를 사용하는 편이 낫다

UNION 연산자는 다른 테이블로부터 읽은 결과셋도 합칠 수 있다는 점에서 다르다

- 하나로 합칠 결과셋은 필드의 개수와 타입이 일치하거나 적어도 호환이 되어야 한다.

- 세개 이상의 테이블을 합칠 수도 있다

 

2. INTERSECT

교집합 연산자, 중복 레코드는 한번만 포함한다 (중복 X)

AND연산자와 다른 점은 역시 다른 테이블 내의 결과셋들을 합칠 수 있다는 점

SELECT name FROM tStaff
INTERSECT
SELECT member FROM tMember;

 

3. MINUS

차집합 연산자. 앞쪽 집합의 원소에서 뒤쪽 집합의 원소를 제외한 집합

개발 중에 테이블의 변화를 관찰하고자 할때 주로 사용

SELECT name FROM tStaff WHERE depart = '영업부'
MINUS
SELECT name FROM tStaff WHERE gender = '여';

SELECT * FROM tStaffNew
MINUS
SELECT * FROM tStaffOld

 

4. MERGE

비교, 삽입, 갱신을 한방에 처리해서 테이블을 병합할 수 있는 명령어(UPSERT라고도 불림)

MERGE INTO tCity T USING tCityNew S ON (S.name = T.name)
WHEN MATCHED THEN
    UPDATE SET T.area = S.area, T.popu = S.popu
    -- DELETE문도 올 수 있다
WHEN NOT MATCHED THEN
    INSERT VALUES (S.name, S.area, S.popu, S.metro, S.region);

ON (S.name = T.name) 부분: 일치조건. 주로 기본키 값으로 비교

두 값이 일치하면 MATCHED THEN 절로 이동, 불일치하면 NOT MATCHED THEN 절로 이동,

 

USING절에 전체 테이블이 아니라 일부 서브쿼리만 와도 된다

MERGE INTO tCity T USING (SELECT * FROM tCityNew WHERE region='경기') S ON (S.name = T.name)
WHEN MATCHED THEN ...
WHEN NOT MATCHED THEN ...

 

 

'SQL' 카테고리의 다른 글

SQL 뽀개기 - 10 | 마무리 실습  (0) 2021.08.23
SQL 뽀개기 - 9 | 조인  (0) 2021.08.23
SQL 뽀개기 - 7 | 모델링  (0) 2021.07.29
SQL 뽀개기 - 6 | 제약조건  (0) 2021.07.24
SQL 뽀개기 - 5 | 삽입, 삭제, 갱신  (0) 2021.07.23
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함