하위 쿼리(SQL Server)

적용 대상:SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceAzure Synapse Analytics AnalyticsPlatform System(PDW)

하위 쿼리는 , INSERT또는 UPDATE문 내부 또는 DELETE 다른 하위 쿼리 내에 SELECT중첩된 쿼리입니다.

이 문서에는 AdventureWorks2022 Microsoft SQL Server 샘플 및 커뮤니티 프로젝트 홈페이지에서 다운로드할 수 있는 샘플 데이터베이스가 필요합니다.

하위 쿼리는 식이 허용되는 모든 위치에서 사용할 수 있습니다. 이 예제에서 하위 쿼리는 문에서 MaxUnitPrice라는 열 식으로 SELECT 사용됩니다.

USE AdventureWorks2022;
GO
SELECT Ord.SalesOrderID, Ord.OrderDate,
    (SELECT MAX(OrdDet.UnitPrice)
     FROM Sales.SalesOrderDetail AS OrdDet
     WHERE Ord.SalesOrderID = OrdDet.SalesOrderID) AS MaxUnitPrice
FROM Sales.SalesOrderHeader AS Ord;
GO

하위 쿼리 기본 사항

하위 쿼리는 내부 쿼리 또는 내부 선택이라고도 하며 하위 쿼리가 포함된 문을 외부 쿼리 또는 외부 선택이라고 합니다.

하위 쿼리를 포함하는 많은 Transact-SQL 문을 조인으로 작성할 수도 있습니다. 다른 질문은 하위 쿼리로만 제기될 수 있습니다. Transact-SQL에서는 일반적으로 하위 쿼리를 포함하는 문과 그렇지 않은 의미상 동등한 버전 간에 성능 차이가 없습니다. SQL Server에서 쿼리를 처리하는 방법에 대한 아키텍처 정보는 SQL 문 처리를 참조하세요. 그러나 존재가 확인되어야 하는 경우에 조인은 더 나은 성능을 생성합니다. 그렇지 않으면 중복을 제거하려면 외부 쿼리의 각 결과에 대해 중첩된 쿼리를 처리해야 합니다. 이런 경우 조인을 사용하면 결과를 더 쉽게 얻을 수 있습니다.

다음 예제에서는 동일한 결과 집합과 실행 계획을 반환하는 하위 쿼리 SELECT 와 조 SELECT 인을 모두 보여 줍니다.

USE AdventureWorks2022;
GO

/* SELECT statement built using a subquery. */
SELECT [Name]
FROM Production.Product
WHERE ListPrice =
    (SELECT ListPrice
     FROM Production.Product
     WHERE [Name] = 'Chainring Bolts' );
GO

/* SELECT statement built using a join that returns
   the same result set. */
SELECT Prd1.[Name]
FROM Production.Product AS Prd1
     JOIN Production.Product AS Prd2
       ON (Prd1.ListPrice = Prd2.ListPrice)
WHERE Prd2.[Name] = 'Chainring Bolts';
GO

외부 SELECT 문에 중첩된 하위 쿼리는 다음과 같은 구성 요소를 갖습니다.

  • 일반 선택 목록 구성 요소를 포함하는 일반 SELECT 쿼리입니다.
  • 하나 이상의 테이블이나 뷰 이름이 포함된 일반 FROM
  • 선택적 WHERE 절입니다.
  • 선택적 GROUP BY 절입니다.
  • 선택적 HAVING 절입니다.

하위 쿼리의 SELECT 쿼리는 항상 괄호로 묶습니다. 또는 FOR BROWSE 절을 COMPUTE 포함할 수 없으며 TOP 절도 지정된 경우에만 절을 포함 ORDER BY 할 수 있습니다.

하위 쿼리는 외부SELECT, INSERTUPDATE문 또는 문의 or HAVING 절 내부 WHERE 또는 DELETE 다른 하위 쿼리 내에 중첩될 수 있습니다. 사용 가능한 메모리 및 쿼리의 다른 식의 복잡성에 따라 제한이 달라지더라도 최대 32개의 중첩 수준이 가능합니다. 개별 쿼리는 최대 32개의 수준 중첩을 지원하지 않을 수 있습니다. 하위 쿼리는 단일 값을 반환할 경우 식을 사용할 수 있는 모든 위치에 나타날 수 있습니다.

테이블이 외부 쿼리가 아닌 하위 쿼리에만 표시되는 경우 해당 테이블의 열을 출력(외부 쿼리의 선택 목록)에 포함할 수 없습니다.

하위 쿼리가 포함된 문은 다음 중 한 가지 형식을 취합니다.

  • WHERE expression [NOT] IN (subquery)
  • WHERE expression comparison_operator [ANY | ALL] (subquery)
  • WHERE [NOT] EXISTS (subquery)

일부 Transact-SQL 문에서 하위 쿼리는 독립적인 쿼리인 것처럼 평가할 수 있습니다. 개념적으로 하위 쿼리 결과는 외부 쿼리로 대체됩니다(SQL Server가 하위 쿼리를 사용하여 Transact-SQL 문을 실제로 처리하는 방법은 아니지만).

세 가지 기본 유형의 하위 쿼리가 있습니다. 있습니다.

  • ANY 또는 ALL에 의해 수정된 비교 연산자나 IN으로 시작하는 목록에서 실행
  • 수정되지 않은 비교 연산자로 시작하고 단일 값을 반환
  • 에 도입된 존재 테스트인 EXISTS가?

하위 쿼리 규칙

하위 쿼리에는 다음과 같은 제한 사항이 적용됩니다.

  • 비교 연산자를 사용하여 도입된 하위 쿼리의 선택 목록에는 식 또는 열 이름 하나만 포함될 수 있습니다(각각 EXISTS 에 대해 SELECT * 작업하거나 IN 목록을 제외).
  • 외부 쿼리의 절에 WHERE 열 이름이 포함된 경우 하위 쿼리 선택 목록의 열과 조인 호환되어야 합니다.
  • ntext, textimage 데이터 형식은 하위 쿼리의 선택 목록에서 사용할 수 없습니다.
  • 단일 값을 반환해야 하므로 수정되지 않은 비교 연산자가 도입한 하위 쿼리(키워드 또는 키워드 ANY 뒤에 오는 하위 쿼리)는 포함 및 HAVING 절을 포함 GROUP BY 할 수 ALL없습니다.
  • 키워드는 DISTINCT 다음을 포함하는 GROUP BY하위 쿼리와 함께 사용할 수 없습니다.
  • COMPUTEINTO 절을 지정할 수 없습니다.
  • ORDER BYTOP을 함께 지정해야만 지정할 수 있습니다.
  • 하위 쿼리를 사용하여 만든 뷰는 업데이트할 수 없습니다.
  • EXISTS로 시작하는 하위 쿼리의 선택 목록은 규칙에 따라 단일 열 이름 대신 별표(*)로 구성됩니다. 함께 도입된 EXISTS 하위 쿼리는 존재 테스트를 만들고 데이터 대신 TRUE 또는 FALSE를 반환하기 때문에 표준 선택 목록의 규칙과 EXISTS 동일합니다.

하위 쿼리에서 열 이름 한정

다음 예제에서는 외부 쿼리 절의 BusinessEntityIDWHERE 이 외부 쿼리 절(Sales.Store)의 테이블 이름으로 FROM 암시적으로 정규화됩니다. 하위 쿼리의 SELECT 목록에서 CustomerID에 대한 참조는 하위 쿼리의 FROM 절, 즉 Sales.Customer 테이블로 한정됩니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Sales.Store
WHERE BusinessEntityID NOT IN
    (SELECT CustomerID
     FROM Sales.Customer
     WHERE TerritoryID = 5);
GO

일반적으로 문의 열 이름은 같은 수준의 FROM 절에서 참조하는 테이블로 암시적으로 한정됩니다. 열이 하위 쿼리 절에서 FROM 참조되는 테이블에 없는 경우 외부 쿼리 절에서 FROM 참조되는 테이블에 의해 암시적으로 정규화됩니다.

이러한 암시적 가정이 지정된 쿼리의 모양은 다음과 같습니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Sales.Store
WHERE Sales.Store.BusinessEntityID NOT IN
    (SELECT Sales.Customer.CustomerID
     FROM Sales.Customer
     WHERE TerritoryID = 5);
GO

테이블 이름을 명시적으로 지정하는 것은 결코 잘못된 일이 아닙니다. 명시적 자격으로 테이블 이름에 대한 암시적 가정을 항상 재정의할 수 있습니다.

Important

하위 FROM 쿼리 절에서 참조하는 테이블에 열이 없지만 외부 쿼리 FROM 절에서 참조하는 테이블에 열이 있는 경우 쿼리는 오류 없이 실행됩니다. SQL Server는 하위 쿼리의 열에 외부 쿼리의 테이블 이름을 암시적으로 한정합니다.

여러 수준의 중첩

하위 쿼리 자체에는 하나 이상의 하위 쿼리가 포함될 수 있습니다. 문에 여러 하위 쿼리를 중첩할 수 있습니다.

다음 쿼리는 영업 사원인 직원의 이름을 찾습니다.

USE AdventureWorks2022;
GO
SELECT LastName, FirstName
FROM Person.Person
WHERE BusinessEntityID IN
    (SELECT BusinessEntityID
     FROM HumanResources.Employee
     WHERE BusinessEntityID IN
        (SELECT BusinessEntityID
         FROM Sales.SalesPerson)
    );
GO

결과 집합은 다음과 같습니다.

LastName                                           FirstName
-------------------------------------------------- -----------------------
Jiang                                              Stephen
Abbas                                              Syed
Alberts                                            Amy
Ansman-Wolfe                                       Pamela
Campbell                                           David
Carson                                             Jillian
Ito                                                Shu
Mitchell                                           Linda
Reiter                                             Tsvi
Saraiva                                            Jos
Vargas                                             Garrett
Varkey Chudukatil                                  Ranjit
Valdez                                             Rachel
Tsoflias                                           Lynn
Pak                                                Jae
Blythe                                             Michael
Mensa-Annan                                        Tete

(17 row(s) affected)

가장 안쪽의 쿼리는 영업 사원 ID를 반환합니다. 이 쿼리보다 한 수준 위의 쿼리는 이러한 영업 사원 ID로 평가하여 직원의 연락처 ID 번호를 반환합니다. 마지막으로 외부 쿼리는 연락처 ID를 사용하여 직원의 이름을 찾습니다.

위의 쿼리를 조인으로 표시할 수도 있습니다.

USE AdventureWorks2022;
GO
SELECT LastName, FirstName
FROM Person.Person c
INNER JOIN HumanResources.Employee e
ON c.BusinessEntityID = e.BusinessEntityID
JOIN Sales.SalesPerson s
ON e.BusinessEntityID = s.BusinessEntityID;
GO

상관 하위 쿼리

대부분의 쿼리는 하위 쿼리를 한 번 실행하고 그 결과 값을 외부 쿼리의 WHERE 절에 대체함으로써 평가됩니다. 상호 관련된 하위 쿼리(반복 하위 쿼리라고도 함)를 포함하는 쿼리에서 하위 쿼리는 해당 값에 대한 외부 쿼리에 따라 달라집니다. 즉, 하위 쿼리는 외부 쿼리에서 선택할 수 있는 각 행에 대해 한 번씩 반복적으로 실행됩니다. 다음 쿼리는 SalesPerson 테이블에서 보너스가 5000이고 EmployeeSalesPerson 테이블에서 직원 ID 번호가 일치하는 각 직원의 성과 이름을 찾습니다.

USE AdventureWorks2022;
GO
SELECT DISTINCT c.LastName, c.FirstName, e.BusinessEntityID
FROM Person.Person AS c JOIN HumanResources.Employee AS e
ON e.BusinessEntityID = c.BusinessEntityID
WHERE 5000.00 IN
    (SELECT Bonus
    FROM Sales.SalesPerson sp
    WHERE e.BusinessEntityID = sp.BusinessEntityID) ;
GO

결과 집합은 다음과 같습니다.

LastName FirstName BusinessEntityID
-------------------------- ---------- ------------
Ansman-Wolfe Pamela 280
Saraiva José 282

(2 row(s) affected)

이 문의 이전 하위 쿼리는 외부 쿼리와 독립적으로 평가할 수 없습니다. Employee.BusinessEntityID에 대한 값이 필요하지만 SQL Server가 Employee다른 행을 검사하면 이 값이 변경됩니다. 이것이 바로 이 쿼리를 평가하는 방식입니다. SQL Server는 각 행의 값을 내부 쿼리로 대체하여 Employee 테이블의 각 행을 결과에 포함시키는 것을 고려합니다. 예를 들어 SQL Server가 행을 Syed Abbas먼저 검사하는 경우 Employee.BusinessEntityID 변수는 값 285를 사용합니다. 이 값은 SQL Server가 내부 쿼리로 대체합니다. 이러한 두 쿼리 샘플은 상관 관계가 있는 하위 쿼리를 사용하여 이전 샘플의 분해를 나타냅니다.

USE AdventureWorks2022;
GO
SELECT Bonus
FROM Sales.SalesPerson
WHERE BusinessEntityID = 285;
GO

결과는 0.00(Syed Abbas 영업 담당자가 아니므로 보너스를 받지 못함)이므로 외부 쿼리는 다음으로 평가됩니다.

USE AdventureWorks2022;
GO
SELECT LastName, FirstName
FROM Person.Person AS c JOIN HumanResources.Employee AS e
ON e.BusinessEntityID = c.BusinessEntityID
WHERE 5000 IN (0.00);
GO

이 값은 false이므로 이전 샘플 쿼리의 결과에 상관 관계가 있는 하위 쿼리가 포함된 행 Syed Abbas 은 포함되지 않습니다. 에 대한 행을 사용하여 동일한 절차를 진행합니다 Pamela Ansman-Wolfe. 결과를 포함하기 때문에 이 행이 결과에 포함되는 것을 볼 수 있습니다 WHERE 5000 IN (5000) .

상관 관계가 있는 하위 쿼리는 외부 쿼리에 있는 FROM 테이블의 열을 테이블 반환 함수의 인수로 참조하여 절에 테이블 반환 함수를 포함할 수도 있습니다. 이 경우 외부 쿼리의 각 행에 대해 테이블 반환 함수는 하위 쿼리에 따라 평가됩니다.

하위 쿼리 유형

하위 쿼리는 다음과 같이 여러 위치에서 지정할 수 있습니다.

테이블 별칭이 있는 하위 쿼리

하위 쿼리와 외부 쿼리가 동일한 테이블을 참조하는 많은 문을 자체 조인(테이블 자체에 조인)으로 지정할 수 있습니다. 예를 들어 하위 쿼리를 사용하여 특정 상태에서 직원의 주소를 찾을 수 있습니다.

USE AdventureWorks2022;
GO
SELECT StateProvinceID, AddressID
FROM Person.Address
WHERE AddressID IN
    (SELECT AddressID
     FROM Person.Address
     WHERE StateProvinceID = 39);
GO

결과 집합은 다음과 같습니다.

StateProvinceID AddressID
----------- -----------
39 942
39 955
39 972
39 22660

(4 row(s) affected)

또는 자체 조인을 사용할 수 있습니다.

USE AdventureWorks2022;
GO
SELECT e1.StateProvinceID, e1.AddressID
FROM Person.Address AS e1
INNER JOIN Person.Address AS e2
ON e1.AddressID = e2.AddressID
AND e2.StateProvinceID = 39;
GO

테이블 별칭 e1 이며 e2 자체에 조인되는 테이블이 서로 다른 두 역할에 표시되기 때문에 필요합니다. 내부 및 외부 쿼리에서 동일한 테이블을 참조하는 중첩된 쿼리에서도 별칭을 사용할 수 있습니다.

USE AdventureWorks2022;
GO
SELECT e1.StateProvinceID, e1.AddressID
FROM Person.Address AS e1
WHERE e1.AddressID IN
    (SELECT e2.AddressID
     FROM Person.Address AS e2
     WHERE e2.StateProvinceID = 39);
GO

명시적 테이블 별칭은 하위 쿼리에서 Person.Address에 대한 참조가 외부 쿼리의 참조와 동일한 것을 의미하지 않는다는 것을 분명히 합니다.

다음을 사용하여 하위 쿼리 IN

IN(또는 NOT IN)으로 시작하는 하위 쿼리의 결과는 값이 0 이상인 목록입니다. 하위 쿼리가 결과를 반환하면 외부 쿼리가 결과를 사용합니다. 다음 쿼리는 Adventure Works Cycles에서 만드는 모든 휠 제품의 이름을 찾습니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID IN
    (SELECT ProductSubcategoryID
     FROM Production.ProductSubcategory
     WHERE [Name] = 'Wheels');
GO

결과 집합은 다음과 같습니다.

Name
----------------------------
LL Mountain Front Wheel
ML Mountain Front Wheel
HL Mountain Front Wheel
LL Road Front Wheel
ML Road Front Wheel
HL Road Front Wheel
Touring Front Wheel
LL Mountain Rear Wheel
ML Mountain Rear Wheel
HL Mountain Rear Wheel
LL Road Rear Wheel
ML Road Rear Wheel
HL Road Rear Wheel
Touring Rear Wheel

(14 row(s) affected)

이 문은 두 단계로 나누어서 계산됩니다. 먼저 내부 쿼리는 이름이 'Wheel'(17)과 일치하는 하위 범주 식별 번호를 반환합니다. 둘째, 이 값은 외부 쿼리로 대체됩니다. 이 쿼리는 하위 범주 식별 번호와 함께 사용되는 제품 이름을 찾습니다 Production.Product.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID IN ('17');
GO

이와 유사한 문제에 대해 하위 쿼리가 아닌 조인을 사용하는 경우의 한 가지 차이점은 조인을 통해 결과에 둘 이상의 테이블의 열을 표시할 수 있다는 점입니다. 예를 들어 결과에 제품 하위 범주의 이름을 포함하려면 조인 버전을 사용해야 합니다.

USE AdventureWorks2022;
GO
SELECT p.[Name], s.[Name]
FROM Production.Product p
INNER JOIN Production.ProductSubcategory s
ON p.ProductSubcategoryID = s.ProductSubcategoryID
AND s.[Name] = 'Wheels';
GO

결과 집합은 다음과 같습니다.

Name
LL Mountain Front Wheel Wheels
ML Mountain Front Wheel Wheels
HL Mountain Front Wheel Wheels
LL Road Front Wheel Wheels
ML Road Front Wheel Wheels
HL Road Front Wheel Wheels
Touring Front Wheel Wheels
LL Mountain Rear Wheel Wheels
ML Mountain Rear Wheel Wheels
HL Mountain Rear Wheel Wheels
LL Road Rear Wheel Wheels
ML Road Rear Wheel Wheels
HL Road Rear Wheel Wheels
Touring Rear Wheel Wheels

(14 row(s) affected)

다음 쿼리는 신용 등급이 양호하고 Adventure Works Cycles가 20개 이상의 항목을 주문하고 평균 배달 리드 타임이 16일 미만인 모든 공급업체의 이름을 찾습니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Purchasing.Vendor
WHERE CreditRating = 1
AND BusinessEntityID IN
    (SELECT BusinessEntityID
     FROM Purchasing.ProductVendor
     WHERE MinOrderQty >= 20
     AND AverageLeadTime < 16);
GO

결과 집합은 다음과 같습니다.

Name
--------------------------------------------------
Compete Enterprises, Inc
International Trek Center
First National Sport Co.
Comfort Road Bicycles
Circuit Cycles
First Rate Bicycles
Jeff's Sporting Goods
Competition Bike Training Systems
Electronic Bike Repair & Supplies
Crowley Sport
Expert Bike Co
Team Athletic Co.
Compete, Inc.

(13 row(s) affected)

내부 쿼리를 계산하여 하위 쿼리 조건을 만족하는 공급업체의 ID를 반환한 후 외부 쿼리를 계산합니다. 내부 쿼리 및 외부 쿼리 모두에 있는 WHERE 절에 둘 이상의 조건을 포함할 수 있습니다.

조인을 사용하면 동일한 쿼리가 다음과 같이 표시됩니다.

USE AdventureWorks2022;
GO
SELECT DISTINCT [Name]
FROM Purchasing.Vendor v
INNER JOIN Purchasing.ProductVendor p
ON v.BusinessEntityID = p.BusinessEntityID
WHERE CreditRating = 1
  AND MinOrderQty >= 20
  AND AverageLeadTime < 16;
GO

조인은 항상 하위 쿼리로 표현될 수 있습니다. 하위 쿼리를 항상 조인으로 표시할 수는 없습니다. 조인은 대칭이기 때문입니다. 테이블 A를 B에 순서대로 조인하고 동일한 대답을 얻을 수 있습니다. 하위 쿼리가 관련된 경우에도 마찬가지입니다.

다음을 사용하여 하위 쿼리 NOT IN

NOT IN 키워드로 시작하는 하위 쿼리도 0개 이상의 값 목록을 반환합니다. 다음 쿼리는 자전거가 완성되지 않은 제품의 이름을 찾습니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID NOT IN
    (SELECT ProductSubcategoryID
     FROM Production.ProductSubcategory
     WHERE [Name] = 'Mountain Bikes'
        OR [Name] = 'Road Bikes'
        OR [Name] = 'Touring Bikes');
GO

이 문은 조인으로 변환할 수 없습니다. 유사하지 않은 조인은 다른 의미를 줍니다. 완성된 자전거가 아닌 일부 하위 범주에 있는 제품의 이름을 찾습니다.

, DELETEINSERT 문의 하위 쿼리 UPDATE

하위 쿼리는 DML(데이터 조작) 문에 SELECT 중첩 INSERTUPDATEDELETE될 수 있습니다.

다음 예에서는 Production.Product 테이블의 ListPrice 열 값을 두 배로 만듭니다. WHERE 절의 하위 쿼리는 Product 테이블에서 업데이트되는 행을 BusinessEntity 1540이 제공하는 행으로만 제한하여 Purchasing.ProductVendor 테이블을 참조합니다.

USE AdventureWorks2022;
GO
UPDATE Production.Product
SET ListPrice = ListPrice * 2
WHERE ProductID IN
    (SELECT ProductID
     FROM Purchasing.ProductVendor
     WHERE BusinessEntityID = 1540);
GO

다음은 조인을 사용하는 동일한 UPDATE 문입니다.

USE AdventureWorks2022;
GO
UPDATE Production.Product
SET ListPrice = ListPrice * 2
FROM Production.Product AS p
INNER JOIN Purchasing.ProductVendor AS pv
    ON p.ProductID = pv.ProductID AND BusinessEntityID = 1540;
GO

동일한 테이블 자체가 다른 하위 쿼리에서 참조되는 경우 명확하게 하기 위해 대상 테이블의 별칭을 사용합니다.

USE AdventureWorks2022;
GO
UPDATE p
SET ListPrice = ListPrice * 2
FROM Production.Product AS p
INNER JOIN Purchasing.ProductVendor AS pv
    ON p.ProductID = pv.ProductID AND BusinessEntityID = 1540;
GO

비교 연산자가 있는 하위 쿼리

하위 쿼리는 비교 연산자(=, < >, >, > =, <, ! >, ! < 또는 < =) 중 하나로 시작할 수 있습니다.

수정되지 않은 비교 연산자를 사용하여 도입된 하위 쿼리(비교 연산자 뒤에 오거나 뒤에 ANY 오지 ALL않음)는 함께 도입된 하위 쿼리와 IN같은 값 목록이 아닌 단일 값을 반환해야 합니다. 이러한 하위 쿼리가 둘 이상의 값을 반환하면 SQL Server는 오류 메시지를 표시합니다.

수정되지 않은 비교 연산자와 함께 도입된 하위 쿼리를 사용하려면 데이터와 문제의 특성에 대해 충분히 잘 알고 있어야 하위 쿼리가 정확히 하나의 값을 반환한다는 것을 알 수 있습니다.

예를 들어 각 영업 담당자가 하나의 판매 지역만 커버하고 Linda Mitchell이 적용되는 지역에 있는 고객을 찾으려는 경우 간단한 = 비교 연산자를 사용하여 도입된 하위 쿼리를 사용하여 문을 작성할 수 있습니다.

USE AdventureWorks2022;
GO
SELECT CustomerID
FROM Sales.Customer
WHERE TerritoryID =
    (SELECT TerritoryID
     FROM Sales.SalesPerson
     WHERE BusinessEntityID = 276);
GO

그러나 Linda Mitchell 둘 이상의 판매 영역을 포함하는 경우 오류 메시지가 표시됩니다. 비교 연산자 = 대신 제형을 IN 사용할 수 있습니다(=ANY 작동).

수정되지 않은 비교 연산자를 사용하여 도입된 하위 쿼리는 단일 값을 반환하기 때문에 집계 함수를 포함하는 경우가 많습니다. 예를 들어 다음 문은 정가가 평균 정가보다 큰 모든 제품의 이름을 찾습니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE ListPrice >
    (SELECT AVG (ListPrice)
     FROM Production.Product);
GO

수정되지 않은 비교 연산자를 사용하여 도입된 하위 쿼리는 단일 값을 반환해야 하므로 또는 절 자체가 단일 값을 반환한다는 것을 알지 GROUP BY 못하면 포함 GROUP BY 하거나 HAVINGHAVING 절을 포함할 수 없습니다. 예를 들어 다음 쿼리는 ProductSubcategoryID 14에 있는 가장 저렴한 제품보다 가격이 높은 제품을 찾습니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE ListPrice >
    (SELECT MIN (ListPrice)
     FROM Production.Product
     GROUP BY ProductSubcategoryID
     HAVING ProductSubcategoryID = 14);
GO

에 의해 수정된 ANY비교 연산자 SOME또는 ALL

하위 쿼리를 도입하는 비교 연산자는 키워드 ALL 또는 ANY. SOME 는 에 해당하는 ISO 표준입니다 ANY. 이러한 비교 연산 자에 대한 자세한 내용은 SOME | 참조 ANY.

수정된 비교 연산자를 사용하여 도입된 하위 쿼리는 0개 이상의 값 목록을 반환하며 또는 HAVING 절을 포함할 GROUP BY 수 있습니다. 이러한 하위 쿼리는 .을 사용하여 EXISTS다시 지정할 수 있습니다.

비교 연산자를 > 예로 > ALL 사용하는 것은 모든 값보다 큰 것을 의미합니다. 즉, 최대값보다 큰 것을 의미합니다. 예를 들어 > ALL (1, 2, 3) 3보다 큰 것을 의미합니다. > ANY 는 하나 이상의 값, 즉 최소값보다 큰 값을 의미합니다. 따라서 > ANY (1, 2, 3) 1보다 큰 것을 의미합니다.

외부 쿼리에 지정된 조건을 충족하는 하위 쿼리 > ALL 의 행의 경우 하위 쿼리를 도입하는 열의 값이 하위 쿼리에서 반환된 값 목록의 각 값보다 커야 합니다.

마찬가지로 > ANY가 있는 행이 외부 쿼리에 지정된 조건을 만족시키려면 하위 쿼리를 시작하는 열의 값이 하위 쿼리에서 반환되는 값 목록에서 하나 이상의 값보다 커야 합니다.

다음 쿼리는 에 의해 ANY수정된 비교 연산자를 사용하여 도입된 하위 쿼리의 예를 제공합니다. 이 쿼리에서는 가격이 제품 하위 범주의 최대 가격보다 크거나 동일한 제품을 찾습니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE ListPrice >= ANY
    (SELECT MAX (ListPrice)
     FROM Production.Product
     GROUP BY ProductSubcategoryID);
GO

각 제품 하위 범주에 대해 내부 쿼리는 최대 정가를 찾습니다. 외부 쿼리는 이러한 모든 값을 살펴보고 개별 제품의 정가가 제품 하위 범주의 최대 정가보다 크거나 같은지 결정합니다. 변경ALL된 경우 ANY 쿼리는 정가가 내부 쿼리에서 반환된 모든 정가보다 크거나 같은 제품만 반환합니다.

하위 쿼리에서 값을 반환하지 않으면 전체 쿼리에서 값을 반환하지 못합니다.

= ANY 연산자는 IN에 해당합니다. 예를 들어 Adventure Works Cycles에서 만드는 모든 휠 제품의 이름을 찾으려면 사용 IN 하거나 = ANY사용할 수 있습니다.

--Using = ANY
USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID = ANY
    (SELECT ProductSubcategoryID
     FROM Production.ProductSubcategory
     WHERE Name = 'Wheels');
GO

--Using IN
USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID IN
    (SELECT ProductSubcategoryID
     FROM Production.ProductSubcategory
     WHERE Name = 'Wheels');
GO

두 쿼리에 대한 결과 집합은 다음과 같습니다.

Name
--------------------------------------------------
LL Mountain Front Wheel
ML Mountain Front Wheel
HL Mountain Front Wheel
LL Road Front Wheel
ML Road Front Wheel
HL Road Front Wheel
Touring Front Wheel
LL Mountain Rear Wheel
ML Mountain Rear Wheel
HL Mountain Rear Wheel
LL Road Rear Wheel
ML Road Rear Wheel
HL Road Rear Wheel
Touring Rear Wheel

(14 row(s) affected)

<> ANY 연산자, 단 NOT IN과 다름:

  • <> ANY not = a, 또는 not = b, 또는 not = c
  • NOT IN not = a, not = b, not = c
  • <> ALL 은 과 같을 수 있습니다. NOT IN

예를 들어 다음 쿼리는 영업 직원이 담당하지 않는 지역에 있는 고객을 찾습니다.

USE AdventureWorks2022;
GO
SELECT CustomerID
FROM Sales.Customer
WHERE TerritoryID <> ANY
    (SELECT TerritoryID
     FROM Sales.SalesPerson);
GO

결과에는 판매 지역이 NULL인 고객을 제외한 모든 고객이 포함됩니다. 고객에게 할당된 모든 지역은 영업 담당자가 적용하기 때문입니다. 내부 쿼리는 영업 담당자가 다루는 모든 판매 지역을 찾은 다음, 각 지역에 대해 외부 쿼리는 하나도 없는 고객을 찾습니다.

이와 같은 이유로 이 쿼리에서 NOT IN을 사용하면 결과에 아무 고객도 포함되지 않습니다.

<> ALL에 해당하는 NOT IN 연산자를 사용해도 동일한 결과를 얻을 수 있습니다.

다음을 사용하여 하위 쿼리 EXISTS

하위 쿼리가 키워드 EXISTS와 함께 도입되면 하위 쿼리는 존재 테스트로 작동합니다. 외부 쿼리의 WHERE 절은 하위 쿼리에서 반환된 행이 있는지 여부를 테스트합니다. 하위 쿼리는 실제로 데이터를 생성하지 않습니다. 의 값을 TRUE 반환합니다 FALSE.

EXISTS에 도입된 하위 쿼리에는 다음 구문이 있습니다.

WHERE [NOT] EXISTS (subquery)

다음 쿼리는 Wheels 하위 범주에 있는 모든 제품의 이름을 찾습니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE EXISTS
    (SELECT *
     FROM Production.ProductSubcategory
     WHERE ProductSubcategoryID =
            Production.Product.ProductSubcategoryID
        AND [Name] = 'Wheels');
GO

결과 집합은 다음과 같습니다.

Name
--------------------------------------------------
LL Mountain Front Wheel
ML Mountain Front Wheel
HL Mountain Front Wheel
LL Road Front Wheel
ML Road Front Wheel
HL Road Front Wheel
Touring Front Wheel
LL Mountain Rear Wheel
ML Mountain Rear Wheel
HL Mountain Rear Wheel
LL Road Rear Wheel
ML Road Rear Wheel
HL Road Rear Wheel
Touring Rear Wheel

(14 row(s) affected)

이 쿼리의 결과를 이해하려면 각 제품의 이름을 차례로 고려합니다. 이 값을 통해 하위 쿼리에서 하나 이상의 행을 반환하는지, 즉, 쿼리에서 존재 테스트의 결과가 TRUE인지 확인합니다.

EXISTS로 시작하는 하위 쿼리는 다음과 같은 점에서 다른 하위 쿼리와 다릅니다.

  • 키워드 EXISTS 앞에 열 이름, 상수 또는 기타 식이 없습니다.
  • 거의 항상 별표(*)로 구성된 하위 쿼리 EXISTS 의 선택 목록입니다. 하위 쿼리에 지정된 조건을 충족하는 행이 있는지 테스트하기 때문에 열 이름을 나열할 이유가 없습니다.

키워드는 EXISTS 하위 쿼리가 없는 대체 공식이 없는 경우가 많기 때문에 중요합니다. EXISTS를 사용하여 만든 일부 쿼리는 다른 방식으로 표현할 수 없지만 많은 쿼리에서 비슷한 결과를 사용하거나 ALL 수정한 ANY 비교 연산자를 사용할 IN 수 있습니다.

예를 들어 앞의 쿼리는 IN을 사용하여 다음과 같이 표현할 수 있습니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE ProductSubcategoryID IN
    (SELECT ProductSubcategoryID
     FROM Production.ProductSubcategory
     WHERE [Name] = 'Wheels');
GO

다음을 사용하여 하위 쿼리 NOT EXISTS

NOT EXISTS는 하위 쿼리에서 반환되는 행이 없는 경우 사용되는 절이 충족된다는 점을 제외하고 WHERE 다음과 같이 EXISTS작동합니다.

예를 들어 휠 하위 범주에 없는 제품의 이름을 찾으려면 다음을 수행합니다.

USE AdventureWorks2022;
GO
SELECT [Name]
FROM Production.Product
WHERE NOT EXISTS
    (SELECT *
     FROM Production.ProductSubcategory
     WHERE ProductSubcategoryID =
            Production.Product.ProductSubcategoryID
        AND [Name] = 'Wheels');
GO

식 대신 사용되는 하위 쿼리

Transact-SQL에서 하위 쿼리는 목록을 제외한 , , 및 문에서 SELECTUPDATE식을 사용할 수 있는 ORDER BY 모든 곳에서 대체될 수 있습니다.DELETEINSERT

다음 예제에서는 이 향상된 기능을 사용하는 방법을 보여 줍니다. 다음 쿼리는 모든 산악용 자전거의 가격, 평균 가격 및 각 산악용 자전거의 가격과 평균 가격 간의 차이를 검색합니다.

USE AdventureWorks2022;
GO
SELECT [Name], ListPrice,
(SELECT AVG(ListPrice) FROM Production.Product) AS Average,
    ListPrice - (SELECT AVG(ListPrice) FROM Production.Product)
    AS Difference
FROM Production.Product
WHERE ProductSubcategoryID = 1;
GO

참고 항목

구문

쿼리 성능 개념