Sub Query: "쿼리 안에 또 다른 쿼리"

""

SELECT col1, (SELECT ...) -- 스칼라 서브쿼리(Scalar Sub Query): 하나의 컬럼처럼 사용 (표현 용도)

FROM (SELECT ...)            -- 인라인 뷰(Inline View): 하나의 테이블처럼 사용 (테이블 대체 용도)

WHERE col = (SELECT ...)-- 일반 서브쿼리: 하나의 변수(상수)처럼 사용 (서브쿼리의 결과에 따라 달라지는 조건절) """

1) Inline view (인라인 뷰)

먼저, FROM 절에 사용하는 서브쿼리부터 살펴볼까요?

위의 설명처럼 인라인 뷰는 SELECT 절의 결과를 FROM 절에서 하나의 테이블처럼 사용하고 싶을 때 사용합니다.

 

기존 단일 쿼리로는 '테이블에서 각 부서별 최대 연봉' 까지 알 수 있었다면, 서브쿼리를 통해서 누가 최대 연봉자인지 확인할 수 있게 되었습니다.

 

 EMP 테이블에서 각 부서별 최대 연봉자 확인  

▶ 인라인 뷰를 먼저 보면, '각 부서별 최대 연봉' 을 구한 결과를 메인 쿼리에서 테이블처럼 사용한 것입니다. 해석해보면, EMP 테이블에서 각 부서별 최대 연봉(인라인 뷰)과 같은 연봉 데이터(e.SAL = i.max_sal)를 갖는 직원 조회. 라고 할 수 있겠죠?

 

인라인 뷰도 테이블처럼 사용하기 때문에 EMP 테이블과 Join 을 하려면 where 절에 조건이 필요하답니다. 그래서 e.DEPTNO = i.DEPTNO 을 써준 것이죠.

 

여기서 주의할 점은 인라인뷰에 있는 max(sal) 컬럼에 Alias 를 명시해주지 않으면, i.max(sal) 로 사용하게 되겠죠. 이렇게 사용하면 오라클은 WHERE 절에서 max 함수로 인식하게 되므로, max_sal 과 같은 Alias 를 사용한 것입니다.

       

추가적으로. 서브쿼리에 ORDER BY 절은 올 수 없답니다. 사실 서브쿼리는 출력 용도가 아닌 테이블처럼 사용 용도이므로 굳이 정렬할 필요가 없는 것이죠.

2) Sub Query (일반 서브쿼리)

일반 서브쿼리는 SELECT 절의 결과를 WHERE 절에서 하나의 변수(상수)처럼 사용하고 싶을 때 사용합니다. 조건절은 서브쿼리의 결과에 따라 달라지겠죠.

 

일반 서브쿼리는 WHERE 절에 사용하는만큼 조건에 필요한 단일 행 서브쿼리와, 다중 행 서브쿼리와 함께 사용됩니다.단일 행 서브쿼리와 다중행 서브쿼리에 따라 연산자를 잘 선택하는 것도 중요하답니다!

 

1. Single Row Sub Query (단일 행 서브쿼리) : 

단일 행 서브쿼리는 수학을 배웠다면 누구나 다 알만한 연산자입니다.

  •  ' = ' : 같다
  •  ' <> ' : 같지 않다
  •  ' > ' : 크다
  •   ' >= ' : 크거나 같다
  •  ' < ' : 작다
  •  ' <= ' : 작거나 같다

EMP 테이블에서 allen 의 연봉보다 높은 연봉을 받는 사람의 정보 출력

Step 1) 먼저 allen 의 연봉을 확인합니다.

Step 2) 위에서 확인한 연봉을 기준으로 쿼리 작성합니다.

▶ Allen 의 연봉이 오른다면 이 식을 계속 사용할 수 없습니다. 변경될 때마다 계속 수정이 필요하죠. 이 때, where 절에 상수를 사용하지 않고, 상수 대신 무언가의 결과를 재사용하고 싶을 때 서브쿼리를 사용합니다!

 

Step 3) 위의 두 쿼리를 합쳐줍니다. 

          * 1600 대신 ALLEN 의 연봉을 조회하는 쿼리문을 넣어줍니다.

▶ WHERE 절에 상수를 사용하지 않았기때문에, 이 식은 더 이상 수정할 필요가 없게되겠죠?

               

2. Multi Row Sub Query (다중 행 서브쿼리) :

  • IN : 같은 값을 찾음
  • >ANY : 최소값을 반환
  • <ANY : 최대값을 반환
  • <ALL : 최소값을 반환
  • >ALL: 최대값을 반환
  •  '=' 은 in 으로 대체
  •  '>' , '<' 은 any, all로 대체 가능

 EMP 테이블에서 A로 시작하는 직원과 같은 부서의 직원 출력

먼저, 이름이 A로 시작하는 직원이 있는 부서를 확인해봅니다.

두 개의 부서가 확인되네요. 비교의 대상이 두 개 이상은 대소비교가 불가합니다. 그럴 경우, in 연산자를 사용합니다. 같은 값을 찾는 연산자죠! 쉽게 풀어보면 아래와 같이 사용할 수 있겠죠? deptno 이 20 혹은 30인 행을 조회

 select *   
 from emp  
 where deptno in (20, 30);

이제, 다중 행 서브쿼리를 작성해보면, 아래와 같습니다.

서브쿼리의 결과가 여러 행일 경우 '=', '<', '>' 와 같은 대소비교가 불가합니다.

10 = 20, 30 (?),
10 > 20, 30(?),
10 < 20, 30(?) 

이럴 때 다중 행 서브쿼리의 in 연산자를 사용해주면 됩니다!

그러면 원하던 결과인 EMP 테이블에서 A로 시작하는 직원과 같은 부서(20, 30)의 직원이 출력되겠죠?

 

EMP 테이블에서 A로 시작하는 직원의 연봉보다 높은 직원 출력         

먼저, A로 시작하는 직원의 연봉을 확인합니다.

이번에도 결과가 두 개죠? 저 두 연봉보다 높은 직원을 조회하려면.. 일단 단일행 비교 연산자를 사용할 수 없겠네요.

select * 
from emp
where sal > min(1100, 1600); 

이렇게 쓰면 Error 입니다!

1100 과 1600 중 최소를 선택하기 위해 min 을 where 절에 사용하면 에러가 발생합니다.

왜냐! 그룹 함수는 where 절에 사용할 수 없기 때문이죠.

이럴 때! 다중 행 서브쿼리의 연산자 any와 all 표현식으로 대체해야합니다.

 

> any(a, b) : a,b 중 최소보다 큰

> all(a, b) : a,b 중 최대보다 큰

< any(a, b) : a,b 중 최대보다 작은

< all(a, b) : a,b 중 최소보다 작은

 

1100, 1600 을 대입해보면,

 

 > any 는 크다와 결합하면 최소를 리턴, 1100 또는 1600 이상이면 1100 리턴

 > all 은 크다와 결합하면 최대를 리턴, 1100 또는 1600 이상이면 1600 리턴

 < any 는 작대와 결합하면 최대를 리턴, 1100 또는 1600 이상이면 1600 리턴

 < all 은 작다와 결합하면 최소를 리턴, 1100 또는 1600 이상이면 1100 리턴

 

너무 헷갈리죠.. 헷갈리면 where 절이 아닌! 서브쿼리에 min, max 를 사용해도 문제 없습니다. 성능상으로 차이가 없기때문에 해석하기 편하고, 이해하기 편하게 작성하는게 좋습니다. 다시 한번, where 절에는 그룹함수 min, max 사용 불가!

select *
from emp
where sal > any(1100, 1600); 

서브쿼리에 min, max 를 사용할 때와, any, all 을 사용할 때의 결과는 당연히 같습니다.

 

<서브쿼리에 min 을 사용한 결과>

                                           <any 를 사용한 결과>

3) Multi Column Sub Query (다중 컬럼 서브쿼리)

다중 컬럼 서브 쿼리도 정말정말 중요하다고 합니다. 여러가지 쿼리들을 비교하면서 살펴보기 위해, '인라인 뷰', '다중 컬럼 서브쿼리',  '상호연관 서브쿼리' 와 비교하면서 진행해봅시다!

 

 Q1. EMP 테이블에서 부서별 최대 연봉자의 이름 조회

 ▶ 마음같아선 다음 식에 ename 을 넣고싶지만.. 문법상 오류로 Error 가 발생합니다. 이를 문제를 해결하기 위해 서브 쿼리를 사용해봅시다!

select deptno, max(sal), ename??(X)  
from emp 
group by deptno;

해결방법 1) Inline View (인라인 뷰) * 대체적으로 인라인뷰가 우수하지만, 간혹! 다중 컬럼 서브쿼리가 좋을 때도 있습니다. 

 ▶ 부서별 최대 연봉 결과를 테이블처럼 사용하였습니다. 그 후, 최대 연봉 결과와 같은 연봉을 가진 직원을 스캔해서 뽑아준 것이죠!

 

해결 방법 2) Multi Column Sub Query (다중 컬럼 서브 쿼리)

 서브 쿼리의 결과가 여러 행이므로 '=' (eqaul) 은 사용 불가합니다. 그래서 in 연산자로 대체! 첫 번째 사용된 서브 쿼리는 생략이 가능한데, 항상 참의 조건이기 때문입니다. 그리고 어차피 두 번째로 사용된 서브쿼리에서 deptno 기준으로 Grouping 해주기때문이죠.

 => 아래는 첫 번째 서브쿼리를 생략한 모습입니다. 하지만.. 가만보니 결과값은 어떨결에 똑같이 나왔지만,

      이 식은 SAL 컬럼만 비교하므로, 부서 별 비교도 필요합니다. 즉. DEPTNO 과 SAL 동시에 비교해야할 필요가 있죠

      이럴 때 동시에 컬럼을 비교하기 위한 비교식이 다중 컬럼 서브 쿼리라는 것입니다~!

 아래와같이 부서번호, 급여를 세트로 비교해주는 것입니다! 이게 바로 다중 컬럼 서브 쿼리라는 것입니다~!

 

                          

 

Q2. STUDENT 테이블에서 각 학년별로 몸무게가 가장 많이 나가는 학생의 이름, 학년, 몸무게 출력

1) Inline View (인라인 뷰)

▶ 서브 쿼리에서 학년별 가장 많이 나가는 몸무게를 구한 후, 테이블처럼 사용합니다. 서브 쿼리와 JOIN을 해주고 학년(GRADE), 무게(WEIGHT) 를 비교해주는 모습입니다.

 

2) Multi Column Sub Query (다중 컬럼 서브 쿼리)

 서브 쿼리에서 학년별 가장 많이 나가는 몸무게를 구한 후, 학년(GRADE), 무게(WEIGHT) 를 세트로 비교해주는 것입니다! 인라인 뷰 보다 뭔가 더 간결해보이지만.. 뭔가 헷갈리고 어렵죠?ㅠㅠ

Q3. STUDENT 테이블에서 각 학년별로 평균 몸무게보다 많이 나가는 학생의 이름, 학년, 몸무게 출력

1) Inline View (인라인 뷰)  * 인라인 뷰는 그룹별 대소비교가 가능

 서브 쿼리에서 학년별 평균 몸무게를 구한 후, 테이블처럼 사용합니다. 서브 쿼리와 JOIN을 해주고 학년(GRADE), 무게(WEIGHT) 를 비교해주는 모습입니다. 다만, 앞 예시에서는 equal 로 비교했다면 여기서는 대소비교를 하는데, 인라인 뷰에서는 그룹별 equal 비교와 대소비교가 모두 가능하다는 것을 알 수 있습니다.

 

2) Multi Column Sub Query (다중 컬럼 서브 쿼리) 다중 컬럼 스브 쿼리는 그룹별 대소비교 불가능

 서브 쿼리에서 학년별 평균 몸무게를 구한 후, 학년(GRADE), 무게(WEIGHT) 를 세트로 비교해주는 것입니다! 하지만 실행해보면 '연산자의 지정이 부적합합니다' 라는 Error 메시지가 뜨게 됩니다. 즉, 멀티 컬럼 서브 쿼리로는 그룹별 대소비교가 불가능하다는 뜻이죠! 이 해결 방안으로, 상호연관 서브쿼리라는 것이 있답니다.

 

 

3) 상호연관 서브쿼리  상호연관 서브쿼리는 그룹별 대소비교가 가능

 메인 쿼리와 서브 쿼리가 계속 정보를 주고 받는다는 의미 상호연관 서브쿼리라고 합니다. 다만.. 계속 모든 행마다 정보를 주고받고, 그룹 연산을 각 행마다 반복하므로 데이터의 양이 많아질수록 불리한 쿼리겠죠. 반대로 소수의 데이터에는 상당히 빠른 속도, 좋은 성능을 보여준다고 합니다. 초록색 줄이 쳐져있는 's1.grade = s2.grade' 이 부분에서 각 테이블의 모든 건수(행)마다 반복됩니다. 메인 쿼리의 s1.grade 값을 모든 행마다 요청하면서  s2.grade 값과 비교하는 것이죠. 그러면서 평균 몸무게보다 높은 정보가 발견되면 출력해주는 것이겠죠?

 

 

이 예시에서는 각 학년에 대한 정보를 구하므로(grade 를 계속해서 비교) 학년에 대한 그룹별 연산이 필요 없습니다. 그러므로 group by grade 문은 생략 가능합니다!

4) Scalar Sub Query (스칼라 서브쿼리)

스칼라 서브쿼리는 select 절에 사브쿼리를 사용하여 하나의 컬럼처럼 사용하기 위한 목적이 있습니다. 조인(Join)의 대체 표현식으로도 자주 사용됩니다. 하지만, 성능도 좋은 편이 아니고, 주 표현식이 아니므로 참고만 해주시면 되겠습니다!

 

Q1. EMP 테이블과 DEPT 테이블을 사용하여 각 직원의 이름, 부서번호, 부서명 출력

1) JOIN

 EMP 테이블과 DEPT 테이블을 조인(JOIN) 한 결과입니다. JOIN 이 이해가 안 가신다면 아래 링크를 참고해주세요!

2) Scalar Sub Query  

 스칼라는 하나의 라는 의미를 가지고 있습니다. 그렇듯, 서브쿼리의 결과가 하나이길 기대하는 쿼리이죠. 결과는 당연히 조인(JOIN)과 똑같지만, FROM 절에 DEPT 테이블을 사용하지 않고 필요한 데이터만 추출해서 바로 SELET 절에 사용해 준 모습입니다.

 

  

 Q2. EMP 테이블과 DEPT 테이블을 사용하여 각 직원의 이름, 부서번호, 부서명 출력

1) Scalar Sub Query  

 아우터 조인(Outer Join) 에서 사용했던 예시인데, 아우터 조인은 null 값이 생략되는 정보도 포함해서 표시하기 위한 조인이라고 했었죠?

 

그러나! 여기서는 아우터 조인을 사용하지 않았는데, 왜? 어떻게? 매니저(선배 직원)가 없는 직원도 출력되는지 의문이 들 것입니다. 아우터 조인을 사용하지 않았지만, 아우터 조인이 적용된 것 처럼 된 것은 바로바로~~ 메인 쿼리에 where 절이 없으므로 모든 행이 서브쿼리로 넘어갈 것입니다. 

 

그렇게 되면 서브쿼리는 null 값을 받게 되고 당연히 null 값을 null 로 출력해주게 되겠죠? 이렇게 마치 아우터 조인을 적용한 것처럼 보이는 것이랍니다!

출처 : https://data-make.tistory.com/25

'CS > DB' 카테고리의 다른 글

DB [SQL Server] - IN / EXISTS / NOT IN / NOT EXISTS  (0) 2020.05.31

이번 포스팅에서는 IN, EXISTS, NOT IN, NOT EXISTS 에 대해서 보다 상세하게 알아보려고 합니다.

해당 내용은 꼭 SQL Server 뿐만 아니라 MySQL 등에서도 포괄적으로 적용되는 내용입니다.

0. 데이터 세팅

먼저 각 구문에 대해서 비교를 할 때 보다 쉽게 확인할 수 있도록 가상 데이터를 세팅해보도록 하겠습니다. 총 2개의 테이블을 생성하며 각 테이블의 이름과 데이터는 아래와 같습니다.

SELECT * FROM TB_FOOD;

SELECT * FROM TB_FOOD;

SELECT * FROM TB_COLOR;

1. IN

SELECT * FROM TB_FOOD f
WHERE f.number IN (SELECT c.number FROM TB_COLOR c);

 

이는 우리가 어느정도 예상할 수 있는 결과입니다. 하지만 실제로 IN 을 포함한 쿼리가 어떻게, 어떤 식으로 작동되는지 알아야 이후에 EXISTS 또는 NOT IN / NOT EXISTS와 헷갈리지 않습니다.

 

위의 쿼리에서는 제일먼저 TB_COLOR 테이블에 접근하게 됩니다. 즉, IN 뒤에 있는 괄호의 서브쿼리를 먼저 실행해서 그에 대한 요소를 가져오는 것이죠. 따라서 사실 IN뒤에 괄호안에는 서브쿼리 이외에도 직접 요소값을 적어줄 수 있습니다.

 

이후에는 TB_FOOD에서 하나의 레코드를 가져오며 그 레코드의 number 값이 앞에서 가져온 IN 이하의 요소들에 포함되어 있는지를 체크합니다. 그리고 IN 이하의 요소들 중 하나라도 일치한다면 그 레코드를 출력하게 되는 것이죠.

 

여기서 중요한 것은, 쿼리에서 TB_COLOR에 먼저 접근하여, number 값들을 가져와 리스트로 IN 이하에 뿌려주고, 그 이후에 TB_FOOD에서 하나의 레코드씩 IN 이하의 요소들과 일치하는지 비교한다는 것 입니다.

2. EXISTS

그럼 EXISTS는 어떻게 동작하는지 쿼리와 그 결과를 보도록 합시다.

SELECT * FROM TB_FOOD f
WHERE EXISTS (SELECT c.number FROM TB_COLOR c);

 

무언가 이상합니다. 우리가 기대했던 결과와는 달리 TB_FOOD 테이블이 그대로 출력되었습니다. 왜 이럴까요? 이는 EXISTS 구문에 대해서 정확히 알지 못하고 잘못 사용하였기 때문에 나온 결과입니다.

 

위의 쿼리를 기준으로 DB가 어떻게 동작하는지 한번 알아보겠습니다.

IN구문에서는 IN 이후에 나오는 소괄호 내부의 서브쿼리에 대해서 먼저 접근하였습니다. 하지만 EXISTS 구문에서는 다릅니다. 먼저 TB_FOOD에 접근하여 하나의 레코드를 가져오고 그 레코드에 대해서 EXISTS 이하의 서브쿼리를 실행하고 서브쿼리에 대한 결과가 '존재하는지'를 확인합니다.

 

예시를 들어 생각해보면, 제일 처음에 [ 1 / 치킨 ] 이라는 레코드를 가져왔을 것이고, 해당 레코드에 대해서 SELECT c.number FROM TB_COLOR c 쿼리를 통해 결과가 나오는지 확인합니다. 이때 서브쿼리에 대해 어떠한 결과라도 존재하기만 한다면 참이 되어서 [ 1 / 치킨 ] 레코드가 출력됩니다.

 

그런데 SELECT c.number FROM TB_COLOR c 쿼리는 사실 TB_FOOD의 어떠한 레코드하고도 연관이 없이 항상 결과값을 가지는 쿼리입니다. 따라서 TB_FOOD의 모든 레코드가 출력되는 것이죠. 그럼 이를 우리가 기대하는 결과대로 출력하도록 하기 위해서는 다음과 같이 쿼리를 수정하면 됩니다.

SELECT * FROM TB_FOOD f
WHERE EXISTS (SELECT c.number FROM TB_COLOR c WHERE c.number = f.number);

이렇게 나온 결과는 사실 IN 구문과 같은 결과를 출력합니다. 하지만 내부적으로 쿼리가 동작하는 방식은 아예 다르다는 것에 주의하시길 바랍니다. 그러한 내부 로직에 따라서 성능차이도 크게 발생하기 때문입니다.

3. NOT IN

이번에는 NOT IN 구문입니다. 먼저 쿼리와 그 결과를 보고 함께 생각해보겠습니다.

SELECT * FROM TB_FOOD f
WHERE f.number NOT IN (SELECT c.number FROM TB_COLOR c);

위의 쿼리를 실행하니 위의 사진과 같이 아무런 결과도 출력되지 않았습니다. 왜 그럴까요?

 

우리가 처음에 알아본 IN의 방식에 대해서 알아봅시다. IN은 먼저 소괄호의 서브쿼리를 실행합니다. 그럼 SELECT c.number FROM TB_COLOR c 의 쿼리가 실행되고 그 결과로 다음의 리스트가 반환됩니다.

( 1, 2, 3, 4, 5, 6, NULL )

즉, 초기의 쿼리는 다음과 같은 쿼리인 것입니다.

SELECT * FROM TB_FOOD f
WHERE f.number NOT IN ( 1, 2, 3, 4, 5, 6, NULL );

그럼 이제 TB_FOOD에서 하나의 레코드씩 가져올 것이고 IN이 아니라 NOT IN 구문이기 때문에 소괄호의 요소들과 일치하지 않아야 결과로 반환됩니다.

 

TB_FOOD의 레코드들 중에서 [ 7 / 사탕 ] 레코드를 예로 들어서 생각해보면 해당 레코드의 number 값인 7이 NOT IN 이하의 소괄호에 있는지 확인하면 됩니다. 분명히 7이라는 요소는 존재하지 않습니다. 따라서 우리의 생각대로 라면 해당 레코드는 결과로 출력되어야 하는데 위에서 본 것 처럼 출력되지 않았습니다. 왜 일까요?

 

이는 DB 에서 해당 요소가 NOT IN 이하의 소괄호의 요소들에 대한 포함여부를 어떻게 판단하는지를 알면 쉽게 이해할수 있습니다. 사실 위의 쿼리는 아래의 쿼리와 같이 동작함으로써 NOT IN 이하의 소괄호에 대한 포함 여부를 판단하게 됩니다.

SELECT * FROM TB_FOOD f
WHERE f.number != 1
AND f.number != 2
AND f.number != 3
AND f.number != 4
AND f.number != 5
AND f.number != 6
AND f.number != NULL;

즉, NOT IN 구문은 TB_FOOD에서 가져온 레코드의 number 값이 소괄호의 모든 요소들과 일치하지 않는지를 체크하는 것입니다. 그런데 위에서는 number 값이 NULL과 연산을 진행하게 되는데, 이때 NULL과의 비교연산은 항상 UNKNOWN 값을 반환하게 됩니다. 따라서 WHERE 절 이하가 TRUE가 아니므로 해당 레코드가 출력되지 않게 되는 것이죠.

 

이렇게 NOT IN이 어떻게 동작하는지를 알고보니 결국 TB_COLOR에 존재하는 NULL 때문에 우리가 기대하던 결과가 나오지 않음을 알 수 있었습니다. 그럼 우리가 기대한 결과가 나오게 하려면 다음과 같이 쿼리를 수정하면 됩니다.

SELECT * FROM TB_FOOD f
WHERE f.number NOT IN (SELECT c.number 
		FROM TB_COLOR c WHERE c.number IS NOT NULL);

4. NOT EXISTS

그럼 마지막으로 NOT EXISTS에 대해서 알아보도록 하겠습니다.

오히려 NOT EXISTS는 위에서 EXISTS에 대해서 이해했다면 크게 어려운 점이 없습니다. 하지만 그 결과가 NOT IN과 약간 다르죠. 쿼리와 결과를 먼저 보도록 하겠습니다.

SELECT * FROM TB_FOOD f
WHERE NOT EXISTS (
		SELECT c.number FROM TB_COLOR c WHERE c.number = f.number);

위에서 NOT IN을 사용했을 때에는 number 값이 NULL인 레코드는 출력되지 않았습니다. 그 이유를 다시 생각해보자면, IN 구문은 요소간에 비교 연산으로 레코드가 출력되는데 NULL 값에 대한 비교연산은 항상 UNKNOWN을 반환하기 때문이었습니다.

 

하지만 앞에서 알아볼 때 EXISTS 구문은 다르게 동작했습니다. 위 쿼리를 기준으로 한다면 먼저 TB_FOOD에서 레코드를 가져오고 해당 레코드의 number를 NOT EXISTS 이하의 서브쿼리에 전달하여 해당 서브쿼리에서 값이 존재하는지를 확인합니다. EXISTS 구문이었다면 값이 존재할 때 해당 레코드를 출력하지만, NOT EXISTS 구문이기에 해당 서브쿼리의 값이 존재하지 않으면 해당 레코드를 출력합니다.

 

여기서 NULL이 출력하는 과정을 한번 더 자세하게 알아보자면, [ NULL / 타코 ] 레코드를 예로 들었을 떄, number = NULL 입니다. 따라서 NOT EXISTS 이하의 쿼리를 확인해보면 다음과 같을 것 입니다.

SELECT c.number FROM TB_COLOR c WHERE c.number = NULL;

이때 우리가 NOT IN에서 알아본 것과 같이 NULL에 대한 비교연산은 항상 UNKNOWN 값을 반환하므로 해당 쿼리의 결과가 존재하지 않게 되고, 이에 따라서 [ NULL / 타코 ] 레코드가 출력되는 것 입니다. 이렇게 IN, EXISTS, NOT IN, NOT EXISTS 에 대해서 알아보았습니다.

 


출처: https://doorbw.tistory.com/222 [Tigercow.Door]

'CS > DB' 카테고리의 다른 글

DB [SQL Server] - Sub Query, Inline View, Scalar, Multi Column  (0) 2020.05.31

+ Recent posts