SQL 데이터베이스에 대해 제대로 알자(끝) - 서브쿼리

BackEnd

지난포스팅 목록

1편(데이터베이스)

2편(조건조합)
3편(정렬)

4편(연산)

5편(추가,삭제,갱신)
6편(논리삭제와 물리삭제)

7편(집계함수)에 이어서 이번에는 서브쿼리에 대해 알아보자

<서브쿼리>

 

서브쿼리는 SELECT 명령에 의한 데이터 질의로 상부가 아닌 하부의 부수적인 질의를 의미한다.

 

서브쿼리는 SQL명령문 안에 지정하는 하부 SELECT 명령으로 괄호로 묶어 지정한다.

 

문법에는 간단하게 SELECT 명령이라고 적었지만 SELECT 구, FROM 구, WHERE 구 등 SELECT명령의 각 구를 기술할 수 있다.

 

특히 서브쿼리는 SQL 명령의 WHERE 구에서 주로 사용된다. WHERE 구는 SELECT, DELETE, UPDATE 구에서 사용할 수 있는데 이들 중 어떤 명령에서든 서브쿼리를 사용할 수 있다.

 

물론 상황에 따라 다른 구에서도 사용할 수 있다. 지금부터 다양한 명령이나 구에서 사용하는 예를 들어가면서 사용법에 대해서 자세하게 알아보도록 하자

 

DELETE의 WHERE 구에서 서브쿼리 사용하기

먼저 DELETE 명령의 WHERE 구에서 서브쿼리를 사용하는 예를 살펴보도록 하자!

 

SELECT * FROM testsql;

 

 

이렇게 테이블이 존재한다고 했을 때

 

 

a 열의 가장 작은 행을 삭제하려고 한다.

 

이 테이블에서는 4개의 행밖에 없으므로 a가 20인 행이 가장작다는 것을 한 눈에 알아볼 수 있다.

 

 따라서 간단하게 

DELETE FROM testsql WHERE a=20;

이라는 명령을 실행해서 제거가 가능하다.

 

하지만 a열의 값이 가장 작은 행이 어느 것인지 전혀 파악할 수 없는 경우에는 어떻게 해야 할까?

 

아마 먼저 SELECT 명령으로 검색하고자 할 것이다.

 

SELECT MIN(a) FROM testsql;

앞의 예제에서 a 열의 최솟값이 20인 것을 알았다. 이 SELECT 명령을 DELETE 명령의 WHERE 구에서 사용하면 하나의 DELETE 명령으로 원하는 행을 삭제할 수 있다. 

 

DELETE 명령과 SELECT 명령을 결합시켜 버리는거다.

DELETE FROM testsql WHERE a=(SELECT MIN(a) FROM testsql);
SELECT * FROM testsql;

서브쿼리를 사용하면 이렇게 DELETE와 SELECT를 결합시킬 수 있다.

괄호로 둘러싼 서브쿼리 부분을 먼저 실행한 후 DELETE 명령을 실행한다고 생각하면 쉽다.

 

한편 SQL에서는 순차형 언어에서처럼 변수가 존재하지 않는다. 만약 변수를 사용할 수 있다고 한다면, 다음과 같이 정리해서 표현할 수 있을 것이다. 

 

변수=(SELECT MIN(a) FROM testsql);
DELETE FROM testsql WHERE a=변수;

 

사실 이렇게 변수를 사용하는 것은 가능하다. 구현방법에는 여러 가지고 있으므로 자세하게 설명할 수는 없지만 변수를 사용할 수 있다는 것을 알고는 있는 것이 좋다.

 

클라이언트 변수 : 

 

앞서 언급한 변수에 관한 것으로, mysql 클라이언트에 한해 다음과 같이 구현할 수 있다.

이때 @a가 변수가 되고 set이 변수에 대입하는 명령이 된다.

mysql> set@a = (SELECT MIN(a) FROM testsql);

 

 

스칼라 값

 

서브쿼리를 사용할 때는 그 SELECT 명령이 어떤 값을 반환하는 지 주의할 필요가 있다. 여러가지 패턴 중에서도 다음과 같은 네 가지가 일반적인 서브쿼리 패턴이다.

 

SELECT MIN(a) FROM testsql;
SELECT no FROM testsql;

 

SELECT MIN(a), MAX(no) FROM testsql;
SELECT no, a FROM testsql;


이때 네 가지 중에 첫 번째 예시만 나머지 세 가지와 다르다. 이는 다른 패턴과 달리 하나의 값을 반환하기 때문이다.

'단일 값'으로도 통용되지만 데이터베이스 업계에서는 '스칼라 값'이라 불리는 경우가 많으므로 기억해 두도록 하자

 

Point -> SELECT 명령이 하나의 값만 반환하는 것을 '스칼라 값을 반환한다'라고 한다!

 

스칼라 값을 반환하는 SELECT 명령을 특별 취급하는 이유는 서브쿼리로서 사용하기 쉽기 때문이다.

 

이처럼 스칼라 값을 반환하도록 SELECT 명령을 작성하고자 한다면 SELECT 구에서 단일 열을 지정한다. 

 

복수 열을 반환하도록 하면 패턴3번이나 4번이 되어버리기 때문이다.

 

SELECT 구에서 하나의 열을 지정하고, GROUP BY를 지정하지 않은 채 집계함수를 사용하면 결과는 단일한 값이 된다.

만약 GROUP BY로 그룹화를 하면 몇가지의 그룹으로 나뉘어져 버릴 가능성이 있기 때문에 결과적으로 단일한 값이 반환되지 않을 수 있다.

 

또한, WHERE 조건으로 하나의 행만 검색할 수 있다면 단일 값이 되므로 스칼라 값을 반환하는 SELECT 명령이 된다.

 

서브쿼리를 사용하기 간편한 경우에 관해서는 아직 언급하지 않았지만 조금이나마 이해되지 않았을까 싶다.

통상적으로 특정한 두 가지가 서로 동일한지 여부를 비교할 때는 서로 단일한 값으로 비교한다. 즉, WHERE 구에서 스칼라 값을 반환하는 서브쿼리는 = 연산자로 비교할 수 있다는 뜻이다.

 

그럼 지금부터 DELETE 명령을 다시 살펴보자

 

DELETE FROM testsql WHERE a=(SELECT MIN(a) FROM testsql);

여기에서 서브쿼리 부분은 스칼라 값을 반환하는 SELECT 명령으로 되어 있으므로 = 연산자를 사용해 열 a의 값과 비교할 수 있다.

반대로 스칼라 값을 반환하지 않도록 만들기란 간단하다. 

 

서브쿼리 부분을 변경하면 스칼라 값을 반환하지 않도록 할 수 있다. SELECT 구에서 다른 열을 지정하거나 GROUP BY를 지정하면 바로 에러가 발생한다.

 

Point -> = 연산자를 사용하여 비교할 경우에는 스칼라 값끼리 비교할 필요가 있다!

 

스칼라 값을 반환하는 서브쿼리를 특별히 '스칼라 서브쿼리'라 부르기도 한다.

앞서 HAVING 구를 설명할 때 '집계함수는 WHERE 구에서는 사용할 수 없다' 라고 설명했다. 하지만 '스칼라 서브쿼리'라면 WHERE 구에 사용할 수 있으므로 집계함수를 사용해 집계한 결과를 조건식으로 사용할 수 있다.

 

그와 비슷한 문제로 'GROUP BY에서 지정한 열 이외의 열을 SELECT 구에 지정하면 에러가 된다' 라는 것도 있었다.

 

하나의 그룹에 다른 값이 여러개 존재할 경우는 스칼라 값이라고 할 수 없다.

 

SELECT 구에서 서브쿼리 사용하기

 

앞서 언급한 예제에서는 WHERE 구에 서브쿼리를 사용했다. 그 밖에도 서브쿼리는 SELECT 구, UPDATE의 SET 구 등 다양한 구 안에서 지정할 수 있다.

 

문법적으로 서브쿼리는 '하나의 항목'으로 취급한다. 단, 문법적으로는 문제없지만 실행하면 에러가 발생하는 경우가 자주 있다.

이는 스칼라 값의 반환여부에 따라 생기는 현상으로, 서브쿼리를 사용할 때는 스칼라 서브쿼리로 되어있는지 확인해야 한다.

 

SELECT 구에서 서브쿼리를 지정할 때는 스칼라 서브쿼리가 필요하다.

 

그럼 이번에는 SELECT 구에서 스칼라 서브쿼리를 사용해보자

SELECT
(SELECT COUNT(*) FROM tm_post) AS sq1, (SELECT COUNT(*) FROM testsql) AS sq2;

 

tm_post 테이블의 행 개수와 testsql 테이블의 행 개수를 각 서브쿼리로 구한다. 여기서 한 가지 주의할 점이 있는데

서브쿼리가 아닌 상부의 SELECT 명령에는 FROM 구가 없다는 것이다. MySQL 등에서는 실제로 FROM 구를 생략할 수 있다.

 

하지만 Oracle 등 전통적인 데이터베이스 제품에서는 FROM 구를 생략할 수 없다. 이때 Oracle에서는 다음과 같이 FROM DUAL로 지정하면 실행할 수 있다. DUAL은 시스템 쪽에서 데이터베이스에 기본으로 작성되는 테이블이다.

 

*(오라클버전)

SELECT
    (SELECT COUNT(*) FROM tm_post) AS sq1, (SELECT COUNT(*) FROM testsql) AS sq2 FROM DUAL;

SET 구에서 서브쿼리 사용하기

 

UPDATE의 SET 구에서도 서브쿼리를 사용할 수 있다. SET 구에서 서브쿼리를 사용해 갱신하는 예를 살펴보자

 

기존 테이블 데이터

UPDATE testsql SET a= (SELECT MAX(a) FROM testsql);
SELECT * FROM testsql;

SET 구에서 서브쿼리를 사용할 경우에도 스칼라 값을 반환하도록 스칼라 서브쿼리를 지정할 필요가 있다.

 

이런 명령도 가능하다 라는 것을 보여주는 예시이다 보니 실질적으로는 별로 쓰지 않는 UPDATE 명령이 되어버렸다. 어쨋든 UPDATE 명령을 실행하면 a 열 값이 모두 a 열의 최댓값으로 갱신된다.

 

사실 이런 경우, 서브쿼리는 상부의 UPDATE 명령과 관련이 있는 조건식으로 지정되지 않으면 별 의미가 없다. 이에 관해서는 차차 설명하도록 하겠다.

 

FROM 구에서 서브쿼리 사용하기

 

FROM 구에서도 서브쿼리를 사용할 수 있다. 지금까지는 FROM 구에서 테이블 지정만 해왔지만 이번에는 FROM 구에 테이블 이외의 것도 지정해보겠다. 

 

FROM 구에 서브쿼리를 지정하는 경우에도 서브쿼리의 기술방법은 같다. 괄호로 SELECT 명령을 묶으면 된다. 다만 FROM 구에는 기본적으로 테이블을 지정하는 만큼 다른 구와는 조금 상황이 다르다.

 

한편 SELECT 구나 SET 구에서는 스칼라 서브쿼리를 지정해야 하지만 FROM 구에 기술할 경우에는 스칼라 값을 반환하지 않아도 좋다.

 

물론 스칼라 값이라도 상관없다.

 

SELECT * FROM (SELECT * FROM testsql) sq;

 

SELECT 명령 안에 SELECT 명령이 들어있는 듯 보인다. 이를 '네스티드(nested)구조', 또는 '중첩구조'나 '내포구조'라 부른다.

 

sq는 테이블의 별명으로 Sub Query의 이니셜에서 따왔다. 3장에서 설명했듯 SELECT 구에서는 열이나 식에 별명을 붙일 수 있다.

 

마찬가지로 FROM 구에서는 테이블이나 서브쿼리에 별명을 불일 수 있다. 

 

테이블에는 이름이 붙어져 있지만 서브쿼리에는 이렇다 함 이름이 붙여져 있지 않다. 별명을 붙이는 것으로 비로소 서브쿼리의 이름을 지정한다. 이 때도 SELECT 구에서 별명을 붙일 때 처럼 'AS' 키워드를 사용하여 지정한다.

 

단, Oracle에서는 AS를 붙이면 에러가 발생한다. Oracle에서는 AS를 붙이지 않는다.

 

SELECT * FROM (SELECT * FROM testsql) AS sq;

중첩구조는 몇 단계로든 구성할 수 있다. 3단계 구조로 만들어도 상관없다.

 

SELECT * FROM (SELECT * FROM (SELECT * FROM testsql)sq1)sq2;

 

보충 설명을 하자면 방금 확인한 예제처럼 테이블 한 개를 지정하는데 3단계 중첩구조로 작성하지는 않는다. 의미가 없기 때문이다.

 

어디까지나 중첩구조를 설명하는 예제임을 알아주길 바란다.

 

실제 업무에서 FROM 구에 서브쿼리를 지정하여 사용하는 경우

 

앞서 LIMIT 구에 관해 설명할 때 Oracle에는 LIMIT 구가 없다고 한 내용을 기억할 것이다.

 

ROWNUM으로 행 개수를 제한할 수 있지만, 정렬 후 상위 몇 건을 추출하는 조건을 붙일 수 없었다.

 

이는 ROWNUM의 경우 WHERE 구로 인해 번호가 할당되기 때문이다. 하지만 FROM 구에서 서브쿼리를 사용하는 것으로 Oracle에서도 정렬 후 상위 몇 건을 추출한다는 행 제한을 할 수 있다.

 

*(Oracle에서 LIMIT 구의 대체 명령)

SELECT * FROM (SELECT * FROM testsql ORDER BY a DESC) sq WHERE ROWNUM <=2;

 

INSERT 명령과 서브쿼리

INSERT 명령과 서브쿼리를 조합해 사용할 수도 있다. INSERT 명령에는 VALUES 구의 일부로 서브쿼리를 사용하는 경우와, VALUES 구 대신 SELECT 명령을 사용하는 두 가지 방법이 있다.

 

먼저 VALUES 구의 값으로 서브쿼리를 사용하는 예를 살펴보자 이때 서브쿼리는 스칼라 서브쿼리로 지정할 필요가 있다. 물론 자료형도 일치해야 한다.

SELECT * FROM testsql;

INSERT INTO testsql VALUES(
                           (SELECT COUNT(*) FROM hi),
                           (SELECT COUNT(*) FROM tm_post)
                          );
SELECT * FROM testsql;
SELECT * FROM testsql;

INSERT SELECT

 

이번에는 VALUES 구 대신에 SELECT 명령을 사용하는 예를 살펴보자

 

다만 다음 예에서는 괄호를 붙이지 않아 서브쿼리라고 부르기 어려울 수도 있다.

 

기존 테이블 데이터

 

INSERT INTO testsql SELECT 1, 2;
SELECT * FROM testsql;

 

흔히 'INSERT SELECT'라 불리는 명령으로 INSERT와 SELECT를 합친 것과 같은 명령이 되었다. 

 

해당 예제에서는 SELECT가 결괏값으로 1과 2라는 상수를 반환하므로 

INSERT INTO testsql VALUES(1,2);

라고 작성한 것과 같다.

 

이때 SELECT 명령이 반환하는 값이 꼭 스칼라 값일 필요는 없다. SELECT가 반환하는 열 수와 자료형이 INSERT할 테이블과 일치하기만 하면 된다.

 

INSERT SELECT 명령은 SELECT 명령의 결과를 INSERT INTO로 지정한 테이블에 전부 추가한다. 

SELECT 명령의 실행 결과를 클라이언트로 반환하지 않고 지정된 테이블에 추가하는 것이다. 이 때문에 데이터의 복사나 이동을 할 때 자주 사용하는 명령이다.

 

열 구성이 똑같은 테이블 사이에는 다음과 같은 INSERT SELECT 명령으로 행을 복사할 수도 있다.

 

INSERT INTO testsql SELECT * FROM testsql;

상관 서브쿼리

 

서브쿼리를 사용해 DELETE 명령과 SELECT 명령을 결합할 수 있었다. 스칼라 서브쿼리가 사용하기 쉬운 서브쿼리란 것도 알았다.

 

이번에는 서브쿼리의 일종인 '상관 서브쿼리'를 EXISTS 술어로 조합시켜서 서브쿼리를 사용하는 방법에 관해 알아보자

 

EXISTS (SELECT명령)

 

EXISTS 술어를 사용하면 서브쿼리가 반환하는 결괏값이 있는지를 조사할 수 있다. 특히 EXISTS를 사용하는 경우에는 서브쿼리가 반드시 스칼라 값을 반환할 필요는 없다.

 

EXIST는 단지 반환된 행이 있는지를 확인해보고 값이 있으면 참, 없으면 거짓으로 반환하므로 어떤 패턴이라도 상관없다.

 

그럼 지금부터 두 개의 샘플 테이블을 사용해서 예를 들어 설명해보겠다.

SELECT * FROM sample1;

select * FROM sample2;

여기서 sample1에는 1부터 5까지 데이터가 저장되어있는데 a 열은 문자열형이지만 

값은 모두 NULL이다.

 

이 열을 UPDATE로 갱신하려고 한다.

 

그럼 지금부터 sample2에 no열의 값과 같은 행이 있다면 '있음'이라는 값으로, 행이 없으면 '없음'이라는 값으로 갱신하도록 해보자

 

몇가지 갱신 방법이 있는데 여기서는 WHERE 구에 조건을 지정해서 '있음'으로 갱신하는 경우와 '없음'으로 갱신하는 경우로 나눠서 처리해보자

 

UPDATE sample1 SET a='있음' WHERE ....
UPDATE sample1 SET a='없음' WHERE ....

 

앞의 명령에서 WHERE 부분을 살펴보자. 여기서 단순하게 no=1 처럼 지정하는 방식으로 처리할 수 없다.

 

서브쿼리를 이용해서 sample2에 행이 있는지부터 조사해야한다. 그리고 '있음'인 경우, 행이 존재하는 경우에 대해 참으로 설정한다.

 

즉, 다음과 같이 EXISTS를 사용하면 조건에 맞는 행을 갱신할 수 있다.

 

UPDATE sample1 SET a ='있음' WHERE EXISTS
(SELECT * FROM sample2 WHERE no2= no);

select * from sample1;

 

서브쿼리 부분이 UPDATE의 WHERE 구로 행을 검색할 때마다 차례로 실행되는 느낌이다.

 

서브쿼리의 WHERE 구는 no2=no라는 조건식으로 되어있다. no2는 sample2의 열이고 no는 sample1의 열이다

 

이때 no가 3과 5일때만 서브쿼리가 행을 반환한다.

 

EXISTS 술어에 서브쿼리를 지정하면 서브쿼리가 행을 반환할 경우에 참을 돌려준다. 

 

결과가 한 줄이라도 그 이상이라도 참이 된다. 반면 반환되는 행이 없을 경우에는 거짓이 된다.

 

 

NOT EXISTS

 

'없음'의 경우, 행이 존재하지 않는 상태가 참이 되므로 이때는 NOT EXISTS를 사용한다. NOT을 붙이는 것으로 값을 부정할 수 있다.

 

UPDATE sample1 SET a='없음'
WHERE NOT EXISTS (SELECT * FROM sample2 WHERE no2=no);

select * from sample1;

존재하지 않는 것들은 모두 '없음'으로 갱신되었다.

 

이처럼 서브쿼리를 이용해 다른 테이블의 상황을 판단하고 UPDATE로 갱신할 수 있다. 여기서는 UPDATE 명령을 예제로 다루었지만 SELECT 명령으로도 서브쿼리를 사용할 수 있다.

 

상관 서브쿼리

서브쿼리에는 명령안에 중첩구조로 된 SELECT 명령이 존재한다. 지금부터 '있음'으로 갱신하는 UPDATE 명령을 다시 살펴보자

 

UPDATE sample1 SET a ='있음' WHERE EXISTS
(SELECT * FROM sample2 WHERE no2= no);

UPDATE 명령(부모)에서 WHERE 구에 괄호로 묶은 부분이 서브쿼리(자식)가 된다. 부모 명령에서는 sample1을 갱신한다. 자식인 서브쿼리에서는 sample2 테이블의 no2 열 값이 부모의 no 열 값과 일치하는 행을 검색한다.

 

이처럼 부모 명령과 자식인 서브쿼리가 특정 관계를 맺는 것을 '상관 서브쿼리'라고 부른다.

 

앞서 설명했던 DELETE의 경우에는 상관 서브쿼리가 아니다. 상관 서브쿼리가 아닌 단순한 서브쿼리는 단독 쿼리로 실행할 수 있다.

 

DELETE FROM testsql WHERE a = (SELECT MIN(a) FROM testsql);

SELECT MIN(a) FROM testsql;

 

하지만 상관 서브쿼리에서는 부모 명령과 연관되어 처리되기 때문에 서브쿼리 부분만을 따로 떼어내서 실행시킬 수 없다.

 

UPDATE sample1 SET a= '있음' WHERE EXISTS (SELECT * FROM sample2 WHERE no = no2);

SELECT * FROM sample2 WHERE no2 = no; <- 에러 : no2가 불명확하다.

 

테이블명 붙이기

지금부터는 조금 다른 이야기를 해볼까 한다. sample1과 sample2는 각각 열이 no와 no2로 서로 다르기 때문에 no가 sample1의 열,

no2가 sample2의 열인 것을 알 수 있다. 하지만 만약 두 열이 모두 같은 이름을 가진다면 어떨까? 'WHERE no = no'라고 조건을 지정하면 제대로 동작할까?

 

사실은 양쪽 테이블 모두 no라는 열로 되어있다면 잘 동작하지 않는다.(대부분은 열이 애매하다는 내용의 에러가 발생한다.)

 

그래서 여기서는 예제 테이블을 작성할 때 설명하기 쉽도록 의도적으로 열명을 바꿔줬다. 

 

대신 조금 부자연스러울수 있다. 개인적으로는 같은 데이터라면 같은 이름으로 지정하는 편이 자연스럽다고 생각한다.

 

방금 언급한 사례가 정상적을 처리되려면 열이 어느 테이블의 것인지 명시적으로 나타낼 필요가 있다.

 

테이블 지정은 간단하다. 열명 앞에 '테이블명'을 붙이기만 하면 된다. 예를 들어 no 열이 sample1의 것이라면 'sample1.no'라고 지정한다. 마찬가지로 no2의 경우에는 'sample2.no2'로 지정한다.

 

이것으로 sample1과 sample2가 열 이름이 같아도 제대로 구별되므로 문제없이 실행할 수 있다.

UPDATE sample1 SET a ='있음' WHERE EXISTS(SELECT * FROM sample2 WHERE sample2.no2=sample1.no);

 

IN

 

스칼라 값끼리 비교할 때는 =연산자를 사용한다. 다만 집합을 비교할 때는 사용할 수 없다. IN을 사용하면 집합 안의 값이 존재하는지를 조사할 수 있다. 

 

서브쿼리를 사용할 때 IN을 통해 비교하는 경우가 많다. 더 자세한 내용은 지금부터 sample1과 sample2 테이블을 사용해서 알아보도록 하자

 

sample2에는 3과 5라는 값이 존재한다. 서브쿼리를 사용하지 않고 WHERE 구로 간단하게 처리한다면 다음과 같이 조건을 붙일 수 있다.

WHERE no =3 OR no=5;

이렇게 특정 열의 값이 '무엇 또는(OR) 무엇'이라는 조건식을 지정하는 경우 IN을 사용하면 간단하게 지정할 수 있다.

 
열명 IN(집합)

 

IN에서는 오른쪽에 집합을 지정한다. 왼쪽에 지정된 값과 같은 값이 집합 안에 존재하면 참을 반환한다. 

집합은 상수 리스트를 괄호로 묶어 기술한다. 앞의 WHERE 조건식을 IN을 사용하도록 수정하면 다음과 같다.

 

IN으로 지정한 값이 3과 5밖에 없어 OR로 기술했을 때와 별 차이가 없는 것 같지만, 값을 여러개 지정할 경우에는 조건식이 상당히 깔끔해진다.

 

SELECT * FROM sample1 WHERE no IN (3,5);

한편, 집합 부분은 서브쿼리로도 지정할 수 있다. 상수 리스트 부분을 서브쿼리로 바꿔보자.

 

SELECT * FROM sample1 WHERE no IN (SELECT no2 FROM sample2);

이 같은 경우 서브쿼리는 스칼라 서브쿼리가 될 필요는 없다. 

 

IN의 왼쪽에는 하나의 열이 지정되어 있기 때문이다. IN은 집합 안에 값이 포함되어 있으면 참이 된다. 반면 NOT IN으로 지정하면 집합에 값이 포함되어 있지 않을 경우 참이 된다.

 

IN과 NULL

 

집계함수에서는 집합 안의 NULL 값을 무시하고 처리했다. IN에서는 집합안에 NULL 값이 있어도 무시하지는 않는다.

다만 NULL = NULL을 제대로 계산할 수 없으므로 IN을 사용해도 NULL값을 비교할 수 없다. 

 

즉, NULL을 비교할 때는 IS NULL을 사용해야 한다. 또한 NOT IN의 경우, 집합 안에 NULL 값이 있으면 설령 왼쪽 값이 집합 안에 포함되어 있지 않아도 참을 반환하지 않는다.

 

그 결과는 '불명(UNKNOWN)'이 된다.

 

 

이것으로 서브쿼리에 대해 알아보았다. 지금까지 총 8편으로 나눠서 SQL 즉, 데이터베이스의 기초지식을 배워봤는데

 

확실히 책에 나오는 예제코드나 내용을 하나씩 다루다보니 시간이 꽤나 많이 소요된다.

 

이 책의 후반부에서는 데이터베이스의 객체, 테이블 작성 및 삭제, 제약, 인데스, 집합연산, 설계 등 다양한 주제가 나오지만

 

해당 내용을 따로 포스팅하지는 않을 생각이다.

 

공부하면서 이해가 완벽하게 되거나 이해하는 과정에서 생긴 문제점과 의문사항을 따로 메모해두었다가 따로 포스팅 하는 것이

 

더 옳은 방법으로 학습이 되는 것 같다고 생각해서 그렇다.

 

8편까지 읽어주신분이 계시다면 고생하셨다. SQL 공부에 조금이나마 도움이 되셨기를 바라며 글을 마친다.

 

 

모든 포스팅에 대한 내용은 도서 SQL첫걸음에서 익히고 배운 것들에 대한 내용을 직접 타이핑 하였음을 밝힙니다.