SQL ALL IN子句

6

我一直在搜索,但没有发现什么特别的。

是否有可能有一个SQL查询可以像ALL IN一样起作用?为了更好地说明,以下是一个表结构。

Orders table
OrderItem table (having several columns, but mainly ProductID, OrderID)
ProductGroup table (several columns, but mainly GroupID and ProductID)

我希望编写一个查询,该查询将选择所有属于特定产品组的订单。因此,如果我有一个名为“XYZ”的组,其ID = 10。它有一个ProductID。假设是ProductID01。
有两个订单项的订单已经到达。ProductID01和ProductID02。要查找特定产品组中的所有订单,可以使用简单的SQL,例如:
SELECT bvc_OrderItem.ProductID, bvc_OrderItem.OrderID
From bvc_OrderItem
INNER JOIN bvc_Product_Group_Product with (nolock) ON bvc_OrderItem.ProductID = bvc_Product_Group_Product.ProductID 
WHERE bvc_Product_Group_Product.GroupID = 10

或者我可以使用IN子句来编写

SELECT bvc_OrderItem.ProductID, bvc_OrderItem.OrderID
From bvc_OrderItem
WHERE ProductID IN (
    SELECT ProductID FROM bvc_Product_Group_Product WHERE GroupID=10
)

然而,这将返回所有包含一个或多个ProductIDs的产品组的订单。我需要仅在订单项全部属于Product Group时才返回订单行。
因此,基本上,我需要一个IN子句,如果IN子句中的所有值都与bvc_OrderItem中的行匹配,则被视为匹配。
或者,如果我们使用Join,那么只有左侧的所有行在相应的右表中都有值时,Join才会成功。
如果我能写得更简单些,我会像这样写:
Select ID FROM Table WHERE ID IN (1, 2, 3, 4)

如果表包含所有ID为1、2、3、4的行,则应返回成功。如果这些IN值中有任何一个缺失,则应返回false,并且不应选择任何内容。
你认为这是可能的吗?还是有一种解决方法可以做到这一点?

也许你应该重新表述你的条件,如“订单中不存在任何不属于X组的行”? - Arvo
3
这听起来像是关系除法 - Damien_The_Unbeliever
对不起,也许我没有表达清楚。实际上,我想要所有订单项目都属于同一组。因此,如果订单有SKU1和SKU2,则它们都必须在产品组A中。 - Sameers Javed
2个回答

4
您可以通过多种方式获取订单列表,例如:
SELECT oi.OrderID
FROM bvc_OrderItem oi JOIN
     bvc_Product_Group_Product pgp
     ON oi.ProductID = pgp.ProductId AND
        pgp.GroupID = 10
GROUP BY oi.OrderID
HAVING COUNT(DISTINCT oi.ProductID) = (SELECT COUNT(*)
                                       FROM bvc_Product_Group_Product
                                       WHERE GroupID = 10
                                      );

获取特定产品需要额外的 join。在大多数情况下,订单列表更为有用。
你使用的 ALL IN 同步语法存在问题,它并不能达到你想要的效果。你想选择的是订单。语法如下:
SELECT bvc_OrderItem.ProductID, bvc_OrderItem.OrderID
From bvc_OrderItem
WHERE ProductID ALL IN (SELECT ProductID
                        FROM bvc_Product_Group_Product
                        WHERE GroupID = 10
                       )

这并没有明确指定你想按照OrderId进行分组,而不是其他级别。

然而,SQL语言的基础灵感来自于关系代数。 SELECTJOINWHEREGROUP BY的构造直接与关系代数的基本构造相关。虽然有时候ALL IN的概念很有用,但它可以使用更基本的构件来表达。


“WHERE ProductID ALL IN ......”这个语句在哪个SQL服务器版本中可以使用?我已经尝试在2008和2012上运行,但它们似乎都不支持该选项。 - Sameers Javed
此外,使用带有Count(DISTINCT oi.Product)的分组可能也不起作用。因为一个产品组中可能有100个产品,而一个订单中可能只有两个或四个项目。我们需要确保订单项目都是产品组的一部分。因此,如果两个或所有订单项目都是产品组的一部分,我们将认为它们匹配。如果其中任何一个不属于该组,则我们将认为它们不匹配。谢谢。 - Sameers Javed

0
你可以用这个巧妙的语句来做到:
DECLARE @Items TABLE
    (
      OrderID INT ,
      ProductID INT
    )
DECLARE @Groups TABLE
    (
      ProductID INT ,
      GroupID INT
    )

INSERT  INTO @Items
VALUES  ( 1, 1 ),
        ( 1, 2 ),
        ( 2, 1 ),
        ( 3, 3 ),
        ( 3, 4 )

INSERT  INTO @Groups
VALUES  ( 1, 10 ),
        ( 2, 10 ),
        ( 3, 10 ),
        ( 4, 15 )


SELECT  OrderID
FROM    @Items i
GROUP BY OrderID
HAVING  ( CASE WHEN 10 = ALL ( SELECT   gg.GroupID
                               FROM     @Items ii
                                        JOIN @Groups gg ON gg.ProductID = ii.ProductID
                               WHERE    ii.OrderID = i.OrderID ) THEN 1
               ELSE 0
          END ) = 1

输出:

OrderID
1
2

还有这个(这个更好):

SELECT  OrderID
FROM    @Items i
        JOIN @Groups g ON g.ProductID = i.ProductID
GROUP BY OrderID
HAVING  MIN(g.GroupID) = 10
        AND MAX(g.GroupID) = 10

嗨,感谢您的回复。然而,如果有一个产品属于该组,它将返回true。我们需要的是,只有当所有产品都属于该组时才返回true。我已经测试过了,即使只有一个ProductID属于产品组,它也会返回orderid。我们希望只有当所有订单项(ProductIds)都属于该组时,才不返回OrderID。 - Sameers Javed
@SameersJaved,你说的不对。我的数据中有Order = 3,其中包含2个产品(3、4),3属于10组,4属于15组。但是我的查询结果中没有返回Order = 3。 - Giorgi Nakeuri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接