ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [데이터베이스] SQL 서브 쿼리의 유형 ( IN, NOT IN, EXISTS )
    데이터베이스와 SQL 2023. 1. 29. 17:49

    서브 쿼리는 하나의 SQL문에 포함되는 또 다른 SQL 문으로,  중첩 서브쿼리와 상관 서브 쿼리로 분류 할 수 있다.

    중첩 서브쿼리단독 실행 가능한 형태이며,  상관 서브쿼리외부 테이블 열 참조가 필요한 형태이다. 

     

    서브쿼리는 메인 쿼리의 열을 모두 사용할 수 있지만, 메인 쿼리는 서브 쿼리의 칼럼을 사용할 수 없다. 

     

     

    중첩 서브쿼리는 SELECT절의 비교 연산자에서 많이 사용한다.

    SELECT 
    	ProductID
    ,	UnitPrice
    ,	AvgPrice = (SELECT UnitPrice FROM dbo.[Order Details]) -- 다중 행 반환
    FROM	dbo.[Order Details] As od
    WHERE	OrderID <= 10250;

    위 쿼리 예시에서 SELECT UnitPrice FROM dbo.[Order Details] 절은 다중 행을 반환하게 되는데, 이 때 SELECT 절에 위치할 경우 값 반환이 불가능하다. 즉, 반드시 하나의 값만 반환이 가능해야 한다.

     

    또한 IN (), NOT IN () 과 함께 사용할 수 있다.

    IN비교 연산자 (=)논리 연산자 (OR) 을 결합한 연산자이다.  IN 절에 중복값이 존재할 때 자동으로 중복값이 제거 되는 암시적 Distinct 의 의미를 내포한다. NULL 값이 있을 경우, IS NULL과 IS NOT NULL 만 사용 가능하므로 자동으로 제거 된다.

    -- 집계
    SELECT c.CompanyName
    FROM dbo.Customers AS c
    WHERE CustomerID IN (
    			SELECT CustomerID
    			FROM dbo.Orders AS o
    			GROUP BY o.CustomerID
    			HAVING COUNT(*) > 20
    			);

    NOT IN () 비교 연산자 (<>)논리 연산자 (AND)와 결합한 연산자이다. 따라서 NULL 값이 있을 경우 정합성 문제가 발생 할 수 있기 때문에 NULL 값이 참조되지 않도록 주의 해야 한다. 

    SELECT 
    	Region, * 
    FROM	dbo.Customers
    WHERE	Region NOT IN ('SP', 'WA') -- SP와 WA 가 아닌 경우만 추출
    ;

     만약 NULL 값이 존재할 경우 어떻게 될까? 

    SELECT 'OK'
    WHERE '메로나' NOT IN ('브라보콘', '쌍쌍바', NULL)
    
    	-- 왜 다른가? 어떻게 바뀌는가?
    	SELECT 'OK'
    	WHERE '메로나' <> '브라보콘'	-- T or F?
    		AND '메로나' <> '쌍쌍바'
    		AND '메로나' <> NULL	-- T or F?

    위 쿼리를 실행하면 OK 가 출력 되어야 할 것 같지만 실제 실행을 해 보면 아무것도 출력 되지 않는다. 

    NULL과 비교를 하는 과정에서 unkwon이 되기 때문이다. 

     

    상관 서브 쿼리의 경우에는 SELECT 절, WHERE 조건, HAVING 조건, APPLY , EXISTS연산자와 사용하며, 단독으로 실행이 불가능하다. 

    SELECT
    	CustomerID
    ,	CompanyName = (SELECT CompanyName 
    		FROM dbo.Customers AS c
    		WHERE c.CustomerID = o.CustomerID) -- 서브쿼리 내 SELECT 만 실행 불가
    ,	Freight
    FROM dbo.Orders AS o
    WHERE o.OrderDate >= DATEADD(dd, -7, '19980510');
    
    
    
    -- 집계값 결합
    SELECT
    	CustomerID
    ,	Freight
    ,	AvgFreight = (
    		SELECT AVG(Freight)
    		FROM dbo.Orders AS a
    		WHERE a.CustomerID = o.CustomerID
    		)
    FROM dbo.Orders AS o
    WHERE o.OrderDate >= DATEADD(dd, -7, '19980510');

     

    EXIST는 부분 검색이 가능한 연산자로, 조건을 만족하는 첫 번째 행을 찾게 되면 더 이상 검색을 하지 않는다.

     

    SELECT c.CompanyName
    FROM dbo.Customers AS c
    WHERE c.Country = 'Sweden' 
    	AND EXISTS (SELECT 1 FROM dbo.Orders AS o
    			WHERE o.CustomerID = c.CustomerID
    			AND o.EmployeeID IN (2, 3, 4));

     

    EXISTS 절은 다음과 같이 IN을 사용할 수 있다. 하지만 EXISTS는 단독으로 사용 가능하지만, IN의 경우에는 단독으로 사용이 불가능하여 아래와 같은 수정이 필요하다.

    SELECT c.CompanyName
    FROM dbo.Customers AS c
    WHERE c.Country = 'Sweden' 
          AND c.CustomerID IN (SELECT o.CustomerID FROM dbo.Orders AS o
                           WHERE o.CustomerID = c.CustomerID
                           AND o.EmployeeID IN (2, 3, 4));

    내부 쿼리에 없는 외부 테이블 열 참조 시, Insert / Update / Delete와 결합 되는 경우 주의 해야 한다.

     

     

     

     

     

    반응형

    댓글

Designed by Tistory.