Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL语句选择包含所有值集的组_Sql_Sql Server_Sql Server 2005 - Fatal编程技术网

SQL语句选择包含所有值集的组

SQL语句选择包含所有值集的组,sql,sql-server,sql-server-2005,Sql,Sql Server,Sql Server 2005,在SQL Server 2005中,我有一个带有订单id和产品id的订单详细信息表。我想编写一条SQL语句,查找包含特定订单中所有项目的所有订单。因此,如果订单5包含项目1、2和3,我希望所有其他订单也包含项目1、2和3。另外,如果订单5有两次2和一次3,我希望所有其他订单都有两个2和一个3 我的偏好是它返回完全匹配的订单,但如果更简单/性能更好的话,作为超集的订单是可以接受的 我尝试了如下的自连接,但发现订单中有任何项目,而不是所有项目 SELECT * FROM Order O1 JOIN

在SQL Server 2005中,我有一个带有订单id和产品id的订单详细信息表。我想编写一条SQL语句,查找包含特定订单中所有项目的所有订单。因此,如果订单5包含项目1、2和3,我希望所有其他订单也包含项目1、2和3。另外,如果订单5有两次2和一次3,我希望所有其他订单都有两个2和一个3

我的偏好是它返回完全匹配的订单,但如果更简单/性能更好的话,作为超集的订单是可以接受的

我尝试了如下的自连接,但发现订单中有任何项目,而不是所有项目

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

如果order 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
declare@Orders表
(
idx整数标识(1,1),
OrderID int,
签名varchar(最大值)
)
声明@Items表
(
idx整数标识(1,1),
ItemID int,
数量整数
)
插入@Orders(OrderID)从[Order]中选择OrderID
声明@i int
声明@cnt int
声明@j int
声明@cnt2int
从@Orders中选择@i=0、@cnt=max(idx)
而@i<@cnt
开始
选择@i=@i+1
声明@temp varchar(最大值)
删除@Items
插入@Items(ItemID,Quantity)
挑选
ItemID,
计数(项目ID)
来自OrderItem oi
在o.idx=@i和o.OrderID=oi.OrderID上加入@Orders o
按oi.ItemID分组
按oi.ItemID订购
从@Items中选择@j=min(idx)-1、@cnt2=max(idx)
而@j<@cnt2
开始
选择@j=@j+1
选择@temp=isnull(@temp+',','','')+
'(' + 
转换(varchar,i.ItemID)+
',' + 
转换(varchar,i.Quantity)+
')'
来自@Items i,其中idx=@j
结束
更新@Orders set Signature=@temp,其中idx=@i
选择@temp=null
结束
挑选
o_其他订单ID
来自@Orders o
加入@Orders o_其他
o_其他签名=o.签名
和o_other.OrderID o.OrderID
其中o.OrderID=@OrderID

这假设(根据您问题的措辞)在订单中订购多个相同项目将导致多行,而不是使用
数量
列。如果是后者,只需从
@Items
填充查询中删除
分组依据
,并将
计数(ItemID)
替换为
数量

如果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
                )
如果可以在同一订单上多次使用相同的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
订单签名为的

(
选择O1.Id
,   (
选择“|”+Cast(OD1.ProductId为varchar(10))
从OrderDetails到OD1
其中OD1.OrderId=O1.Id
按OD1.ProductId排序
对于Xml路径(“”)
)作为签名
从订单到O1
)
选择。。。
从OrderSignatures到O
以O2身份加入OrderSignatures
关于O2.签名=O.签名
和O2.Id O.Id
其中O.Id=5

我认为这应该行得通。我使用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的计数与退回订单的产品计数进行比较。

如果该人员订购的物品是#1的倍数,则该物品将作为多行出现在订单详细信息中,正确。没有“数量”列?正确--没有数量列您以“这类事情在SQL中很难做到”开头让我感觉更好(我想他是在试图根据另一个订单ID中包含的项目来匹配它,而不是基于静态的项目列表(每个项目只需一个连接就很容易做到)“亚当·罗宾逊——我明白了。我已经扩展了我的答案,以包括这类请求。我没有想到使用CTE和XML来创建签名。要优雅得多,尽管可能有点难以理解。请注意,这不适用于多个相同项目的订单。例如,如果订单
1
有两个项目
10
和一个项目
20
,而订单
2
有一个项目
10
和两个项目
20
,则此代码将导致匹配。如果计数匹配,则不会发现任何订单只有一些相同的项目吗?因此,如果订单108有3个项目,这将发现任何3个(或更多)项目订单中至少有一个项目来自108?@Adam I将DISTINCT添加到COUNT(DISTINCT…),这将解决该问题@埃迪,不是:这只是比较订单上的商品数量,而不是订单上的所有产品。