簡體   English   中英

用於選擇包含所有一組值的組的SQL語句

[英]SQL statement to select group containing all of a set of values

在SQL Server 2005中,我有一個訂單詳細信息表,其中包含訂單ID和產品ID。 我想編寫一個sql語句,查找包含特定訂單中所有項目的所有訂單。 所以,如果訂單5有第1項,第2項和第3項,我會希望所有其他訂單也有1,2和3.此外,如果訂單5有2次和3次,我希望所有其他訂單兩個2s和一個3。

我的偏好是它返回完全匹配的訂單,但如果更容易/執行得更好,那么作為超集的訂單是可以接受的。

我嘗試了如下所示的自我加入,但是發現任何項目的訂單而不是所有項目。

SELECT * FROM Order O1
JOIN Order O2 ON (O1.ProductId = O2.ProductId)
WHERE O2.OrderId = 5

如果訂單5包含兩次相同的項目,這也給了我重復。

如果OrderDetails表包含OrderId和ProductId的唯一約束,那么您可以執行以下操作:

Select ...
From Orders As O
Where Exists    (
                Select 1
                From OrderDetails As OD1
                Where OD1.ProductId In(1,2,3)
                    And OD1.OrderId = O.Id
                Group By OD1.OrderId
                Having Count(*) = 3
                )

如果可以在同一個Order上多次使用相同的ProductId,那么您可以將Having子句更改為Count(Distinct ProductId) = 3

現在,鑒於上述情況,如果您希望每個訂單具有相同簽名且具有重復產品條目的情況,那就更棘手了。 為此,您需要對相關產品的相關訂單簽名,然后查詢該簽名:

With OrderSignatures As
    (
    Select O1.Id
        ,   (
            Select '|' + Cast(OD1.ProductId As varchar(10))
            From OrderDetails As OD1
            Where OD1.OrderId = O1.Id
            Order By OD1.ProductId
            For Xml Path('')
            ) As Signature
    From Orders As O1
    )
Select ...
From OrderSignatures As O
    Join OrderSignatures As O2
        On O2.Signature = O.Signature
            And O2.Id <> O.Id
Where O.Id = 5

這種事情在SQL中很難做到,因為SQL旨在通過在最基本的級別上將每行的一組列值與另一個值進行比較來生成其結果集。 您要做的是將多行上的單個列值(或列值集)與另一組多行進行比較

為此,您必須創建某種訂單簽名。 嚴格地說,單獨使用查詢語法是不可能的; 你將不得不使用一些T-SQL。

declare @Orders table 
(
    idx int identity(1, 1), 
    OrderID int, 
    Signature varchar(MAX)
)
declare @Items table 
(
    idx int identity(1, 1), 
    ItemID int, 
    Quantity int
)

insert into @Orders (OrderID) select OrderID from [Order]

declare @i int
declare @cnt int

declare @j int
declare @cnt2 int

select @i = 0, @cnt = max(idx) from @Orders

while @i < @cnt
begin
    select @i = @i + 1

    declare @temp varchar(MAX)

    delete @Items

    insert into @Items (ItemID, Quantity)
    select 
        ItemID, 
        Count(ItemID) 

    from OrderItem oi    

    join @Orders o on o.idx = @i and o.OrderID = oi.OrderID

    group by oi.ItemID

    order by oi.ItemID

    select @j = min(idx) - 1, @cnt2 = max(idx) from @Items

    while @j < @cnt2
    begin
        select @j = @j + 1

        select @temp = isnull(@temp + ', ','') + 
            '(' + 
            convert(varchar,i.ItemID) + 
            ',' + 
            convert(varchar, i.Quantity) + 
            ')'
        from @Items i where idx = @j
    end

    update @Orders set Signature = @temp where idx = @i

    select @temp = null
end

select 
    o_other.OrderID 

from @Orders o

join @Orders o_other on 
        o_other.Signature = o.Signature
    and o_other.OrderID <> o.OrderID

where o.OrderID = @OrderID

這假定(基於您的問題的措辭)在訂單中對多個相同項目進行排序將導致多行,而不是使用Quantity列。 如果是后者,只需從@Items填充查詢中刪除group by ,並將Count(ItemID)替換為Quantity

我認為這應該有效。 我使用108作為OrderID的示例,因此您必須在下面替換兩次或使用變量。

WITH TempProducts(ProductID) AS
(
   SELECT DISTINCT ProductID FROM CompMarket
   WHERE OrderID = 108
)
SELECT OrderID  FROM CompMarket 
WHERE ProductID IN (SELECT ProductID FROM TempProducts) 
AND OrderID != 108
GROUP BY OrderID
HAVING COUNT(DISTINCT ProductID) >= (SELECT COUNT(ProductID) FROM TempProducts)

這使用CTE獲取訂單產品的列表,然后選擇所有產品都在此列表中的訂單ID。 為了確保返回的訂單包含所有產品,這會將CTE的計數與退回的訂單產品的計數進行比較。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM