자체 포함 또는 상호 관련된 하위 쿼리 사용

완료됨

앞에서 자체 포함 하위 쿼리를 살펴보았습니다. 이 쿼리에서는 내부 쿼리가 외부 쿼리에 종속되지 않고, 한 번 실행되며, 결과를 외부 쿼리에 반환합니다. T-SQL에서는 내부 쿼리가 외부 쿼리의 열을 참조하고 개념적으로 행당 한 번 실행되는 ‘상호 관련된’ 하위 쿼리도 지원합니다.

상호 관련된 하위 쿼리 사용

자체 포함 하위 쿼리와 마찬가지로 상호 관련된 하위 쿼리는 외부 쿼리 내에 중첩된 SELECT 문입니다. 상호 관련된 하위 쿼리도 스칼라 또는 다중값 하위 쿼리일 수 있습니다. 일반적으로 내부 쿼리에서 외부 쿼리의 값을 참조해야 하는 경우에 상호 관련된 하위 쿼리를 사용합니다.

그러나 자체 포함 하위 쿼리와 달리 상호 관련된 하위 쿼리를 사용할 때 특별히 고려해야 하는 사항이 몇 가지 있습니다.

  • 상호 관련된 하위 쿼리는 외부 쿼리와 별도로 실행할 수 없습니다. 이 제한 사항으로 인해 테스트와 디버깅이 복잡해집니다.
  • 한 번 처리되는 자체 포함 하위 쿼리와 달리 상호 관련된 하위 쿼리는 여러 번 실행됩니다. 논리적으로 외부 쿼리가 먼저 실행되고 반환되는 각 행에 대해 내부 쿼리가 처리됩니다.

다음 예제에서는 상호 관련된 하위 쿼리를 사용하여 각 고객의 최근 주문을 반환합니다. 하위 쿼리는 외부 쿼리를 참조하고 WHERE 절에서 CustomerID 값을 참조합니다. 하위 쿼리는 외부 쿼리의 각 행에 대해 해당 행에서 참조된 고객의 최대 주문 ID를 찾고, 외부 쿼리는 확인 중인 행이 해당 주문 ID가 있는 행인지 확인합니다.

SELECT SalesOrderID, CustomerID, OrderDate
FROM SalesLT.SalesOrderHeader AS o1
WHERE SalesOrderID =
    (SELECT MAX(SalesOrderID)
     FROM SalesLT.SalesOrderHeader AS o2
     WHERE o2.CustomerID = o1.CustomerID)
ORDER BY CustomerID, OrderDate;

상호 관련된 하위 쿼리 작성

상호 관련된 하위 쿼리를 작성하려면 다음 지침을 따르는 것이 좋습니다.

  • 내부 쿼리의 적절한 반환 결과를 수락하도록 외부 쿼리를 작성합니다. 내부 쿼리가 스칼라인 경우 WHERE 절에서 =, <, >, <>, 등의 같음 및 비교 연산자를 사용할 수 있습니다. 내부 쿼리가 여러 값을 반환할 수 있는 경우 IN 조건자를 사용합니다. NULL 결과를 처리하도록 계획합니다.
  • 상호 관련된 하위 쿼리에서 참조할 외부 쿼리의 열을 확인합니다. 외부 쿼리에서 열 원본인 테이블의 별칭을 선언합니다.
  • 외부 테이블의 열과 비교할 내부 테이블의 열을 확인합니다. 외부 쿼리처럼 원본 테이블의 별칭을 만듭니다.
  • 외부 쿼리의 입력값을 기준으로 해당 원본에서 값을 검색하도록 내부 쿼리를 작성합니다. 예를 들어 내부 쿼리의 WHERE 절에서 외부 열을 사용합니다.

내부 쿼리와 외부 쿼리 간의 상관 관계는 내부 쿼리에서 비교를 위해 외부 값을 참조할 때 생깁니다. 이 상관 관계 때문에 상호 관련된 하위 쿼리라고 합니다.

EXISTS 사용

T-SQL에서는 하위 쿼리에서 값을 검색하는 기능 외에 쿼리에서 결과가 반환되는지 여부를 확인하는 메커니즘도 제공합니다. EXISTS 조건자는 지정된 조건을 충족하는 행이 있는지 여부를 확인하지만, 해당 행을 반환하는 대신 TRUE 또는 FALSE를 반환합니다. 이 기법은 결과를 검색 및 처리하는 오버헤드 없이 데이터의 유효성을 검사하는 데 유용합니다.

EXISTS 조건자를 사용하여 하위 쿼리를 외부 쿼리와 연결하면 SQL Server는 하위 쿼리 결과를 특별한 방식으로 처리합니다. EXISTS는 하위 쿼리에서 스칼라 값 또는 다중값 목록을 검색하는 대신 결과에 행이 있는지 여부만 확인합니다.

개념적으로 EXISTS 조건자는 결과를 검색하고, 반환되는 행 수를 계산하며, 해당 개수를 0과 비교하는 것에 해당합니다. 주문한 고객에 대한 세부 정보를 반환하는 다음 쿼리를 비교해 보세요.

첫 번째 예제 쿼리는 하위 쿼리에서 COUNT를 사용합니다.

SELECT CustomerID, CompanyName, EmailAddress 
FROM Sales.Customer AS c 
WHERE
(SELECT COUNT(*) 
  FROM Sales.SalesOrderHeader AS o
  WHERE o.CustomerID = c.CustomerID) > 0;

동일한 결과를 반환하는 두 번째 쿼리는 EXISTS를 사용합니다.

SELECT CustomerID, CompanyName, EmailAddress 
FROM Sales.Customer AS c 
WHERE EXISTS
(SELECT * 
  FROM Sales.SalesOrderHeader AS o
  WHERE o.CustomerID = c.CustomerID);

첫 번째 예제에서 하위 쿼리는 Sales.SalesOrderHeader 테이블에서 찾은 각 custid의 모든 항목 수를 계산하고, 단순히 고객이 주문했음을 나타내기 위해 해당 개수 결과를 0과 비교해야 합니다.

두 번째 쿼리에서 EXISTS는 Sales.SalesOrderHeader 테이블에서 관련 주문을 찾는 즉시 custid에 대해 TRUE를 반환합니다. 각 항목에 대해서는 자세히 고려하지 않아도 됩니다. 또한 EXISTS 형식을 사용하는 하위 쿼리는 단일 열을 반환하도록 제한되지 않습니다. 여기에는 SELECT *가 있습니다. 여기서는 행이 반환되는지만 확인하고 해당 행의 값은 확인하지 않으므로 반환된 열은 관련이 없습니다.

논리적 처리의 관점에서는 두 쿼리 형식이 동일합니다. 성능 관점에서는 데이터베이스 엔진이 실행하기 위해 쿼리를 최적화하므로 두 쿼리를 다르게 처리할 수 있습니다. 고유한 용도에 맞게 각 쿼리를 테스트해 보세요.

참고

COUNT(*)를 사용하는 하위 쿼리를 EXISTS를 사용하는 하위 쿼리로 변환하는 경우 하위 쿼리에서 SELECT COUNT(*)가 아니라 SELECT *를 사용해야 합니다. SELECT COUNT(*)는 항상 행을 반환하므로 EXISTS에서 항상 TRUE를 반환합니다.

다음 예제와 같이 NOT으로 하위 쿼리를 부정하면서 EXISTS를 유용하게 응용하는 방법도 있습니다. 이 경우 주문을 한 번도 하지 않은 고객이 반환됩니다.

SELECT CustomerID, CompanyName, EmailAddress 
FROM SalesLT.Customer AS c 
WHERE NOT EXISTS
  (SELECT * 
   FROM SalesLT.SalesOrderHeader AS o
   WHERE o.CustomerID = c.CustomerID);

SQL Server에서는 주문한 고객의 관련 주문에 대한 데이터를 반환할 필요가 없습니다. Sales.SalesOrderHeader 테이블에 custid가 있으면 NOT EXISTS는 FALSE로 평가되고 평가가 신속하게 완료됩니다.

하위 쿼리에 EXISTS를 사용하는 쿼리를 작성하려면 다음 지침을 따르는 것이 좋습니다.

  • EXISTS 키워드는 WHERE 바로 뒤에 오며, NOT도 함께 사용하지 않는 한 앞에 열 이름(또는 다른 식)이 오지 않습니다.
  • 하위 쿼리 내에서 SELECT *를 사용합니다. 하위 쿼리에서 반환하는 행이 없으므로 열을 지정할 필요가 없습니다.