구체적이지만 가설적인 예를 사용하겠습니다.
각 주문 에는 일반적으로 하나의 광고 항목 만 있습니다 .
명령:
OrderGUID OrderNumber
========= ============
{FFB2...} STL-7442-1
{3EC6...} MPT-9931-8A
광고 항목 :
LineItemGUID Order ID Quantity Description
============ ======== ======== =================================
{098FBE3...} 1 7 prefabulated amulite
{1609B09...} 2 32 spurving bearing
그러나 때때로 두 개의 광고 항목이있는 주문이 있습니다.
LineItemID Order ID Quantity Description
========== ======== ======== =================================
{A58A1...} 6,784,329 5 pentametric fan
{0E9BC...} 6,784,329 5 differential girdlespring
일반적으로 사용자에게 주문을 표시 할 때 :
SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
INNER JOIN LineItems
ON Orders.OrderID = LineItems.OrderID
주문에 단일 항목을 표시하고 싶습니다. 그러나이 가끔 순서가 항목을 두를 포함하는 (또는 그 이상)과 함께 주문 것이라고 표시 할 복제 :
OrderNumber Quantity Description
=========== ======== ====================
STL-7442-1 7 prefabulated amulite
MPT-9931-8A 32 spurving bearing
KSG-0619-81 5 panametric fan
KSG-0619-81 5 differential girdlespring
내가 정말로 원하는 것은 SQL Server 가 충분히 좋기 때문에 하나만 선택 하는 것입니다 .
OrderNumber Quantity Description
=========== ======== ====================
STL-7442-1 7 prefabulated amulite
MPT-9931-8A 32 differential girdlespring
KSG-0619-81 5 panametric fan
모험심이 많으면 사용자에게 둘 이상의 줄이 있음을 나타내는 줄임표를 표시 할 수 있습니다.
OrderNumber Quantity Description
=========== ======== ====================
STL-7442-1 7 prefabulated amulite
MPT-9931-8A 32 differential girdlespring
KSG-0619-81 5 panametric fan, ...
그래서 문제는
- “중복”행 제거
- 중복을 피하기 위해 행 중 하나에 만 조인
첫번째 시도
첫 순진한 시도는 ” TOP 1 “광고 항목 에만 참여하는 것이 었습니다 .
SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
INNER JOIN (
SELECT TOP 1 LineItems.Quantity, LineItems.Description
FROM LineItems
WHERE LineItems.OrderID = Orders.OrderID) LineItems2
ON 1=1
그러나 오류가 발생합니다.
열 또는 접두사 ‘Orders’가
쿼리에 사용 된
테이블 이름 또는 별명과 일치 하지 않습니다
.
내부 선택에 외부 테이블이 표시되지 않기 때문일 수 있습니다.
답변
SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
JOIN LineItems
ON LineItems.LineItemGUID =
(
SELECT TOP 1 LineItemGUID
FROM LineItems
WHERE OrderID = Orders.OrderID
)
위의 SQL 서버 2005에서는, 당신은 대체 할 수 INNER JOIN
와 함께 CROSS APPLY
:
SELECT Orders.OrderNumber, LineItems2.Quantity, LineItems2.Description
FROM Orders
CROSS APPLY
(
SELECT TOP 1 LineItems.Quantity, LineItems.Description
FROM LineItems
WHERE LineItems.OrderID = Orders.OrderID
) LineItems2
주의하시기 바랍니다 TOP 1
없이 ORDER BY
이 쿼리는 당신에게 주문 당 한 줄의 항목을 얻을 것이다, 그러나 하나가 될 것이다 정의되지 않은 : 결정되지 않습니다.
쿼리를 여러 번 호출하면 기본이 변경되지 않은 경우에도 동일한 순서로 다른 광고 항목을 제공 할 수 있습니다.
결정적인 순서를 원하면 ORDER BY
가장 안쪽 쿼리에 절을 추가해야 합니다.
답변
나는이 질문에 얼마 전에 대답 한 것을 알고 있지만 큰 데이터 세트를 처리 할 때 중첩 쿼리에 많은 비용이들 수 있습니다. 다음은 반환 된 각 행 대신 중첩 된 쿼리가 한 번만 실행되는 다른 솔루션입니다.
SELECT
Orders.OrderNumber,
LineItems.Quantity,
LineItems.Description
FROM
Orders
INNER JOIN (
SELECT
Orders.OrderNumber,
Max(LineItem.LineItemID) AS LineItemID
FROM
Orders INNER JOIN LineItems
ON Orders.OrderNumber = LineItems.OrderNumber
GROUP BY Orders.OrderNumber
) AS Items ON Orders.OrderNumber = Items.OrderNumber
INNER JOIN LineItems
ON Items.LineItemID = LineItems.LineItemID
답변
당신은 할 수 있습니다 :
SELECT
Orders.OrderNumber,
LineItems.Quantity,
LineItems.Description
FROM
Orders INNER JOIN LineItems
ON Orders.OrderID = LineItems.OrderID
WHERE
LineItems.LineItemID = (
SELECT MIN(LineItemID)
FROM LineItems
WHERE OrderID = Orders.OrderID
)
인덱스 (또는 기본 키)가 켜져 LineItems.LineItemID
있고 인덱스 LineItems.OrderID
가 있어야합니다. 그렇지 않으면 느려집니다.
답변
@Quassnoi 응답은 경우에 따라 (특히 외부 테이블이 큰 경우) 다음과 같이 창 함수를 사용하여보다 효율적인 쿼리를 수행 할 수 있습니다.
SELECT Orders.OrderNumber, LineItems2.Quantity, LineItems2.Description
FROM Orders
LEFT JOIN
(
SELECT LineItems.Quantity, LineItems.Description, OrderId, ROW_NUMBER()
OVER (PARTITION BY OrderId ORDER BY (SELECT NULL)) AS RowNum
FROM LineItems
) LineItems2 ON LineItems2.OrderId = Orders.OrderID And RowNum = 1
때로는 더 나은 성능을 제공하는 쿼리 를 테스트하기 만하면 됩니다.
답변
공통 테이블 표현식을 사용하는 또 다른 방법 :
with firstOnly as (
select Orders.OrderNumber, LineItems.Quantity, LineItems.Description, ROW_NUMBER() over (partiton by Orders.OrderID order by Orders.OrderID) lp
FROM Orders
join LineItems on Orders.OrderID = LineItems.OrderID
) select *
from firstOnly
where lp = 1
또는 결국 모든 행을 결합하여 표시하고 싶습니까?
쉼표로 구분 된 버전 :
select *
from Orders o
cross apply (
select CAST((select l.Description + ','
from LineItems l
where l.OrderID = s.OrderID
for xml path('')) as nvarchar(max)) l
) lines
답변
SQL Server 2012부터는 트릭을 수행 할 것이라고 생각합니다.
SELECT DISTINCT
o.OrderNumber ,
FIRST_VALUE(li.Quantity) OVER ( PARTITION BY o.OrderNumber ORDER BY li.Description ) AS Quantity ,
FIRST_VALUE(li.Description) OVER ( PARTITION BY o.OrderNumber ORDER BY li.Description ) AS Description
FROM Orders AS o
INNER JOIN LineItems AS li ON o.OrderID = li.OrderID
답변
상관 된 하위 쿼리는 외부 쿼리에 의존하는 하위 쿼리입니다. SQL의 for 루프와 같습니다. 하위 쿼리는 외부 쿼리의 각 행마다 한 번씩 실행됩니다.
select * from users join widgets on widgets.id = (
select id from widgets
where widgets.user_id = users.id
order by created_at desc
limit 1
)
