产品捆绑中的组订单行项目

时间:2018-09-29 19:41:41

标签: sql sql-server tsql

这是我的模式:

CREATE TABLE SampleProducts 
(
    ProductId INT,
    Name NVARCHAR(20)
)

INSERT INTO SampleProducts 
VALUES (1, 'Product 1'), (2, 'Product 2'), (3, 'Product 3'),
       (4, 'Product 4')

CREATE TABLE Bundle  
(
    BundleId INT,
    Name NVARCHAR(20)
)

INSERT INTO Bundle 
VALUES (1, 'Bundle 1'), (2, 'Bundle 2')

CREATE TABLE BundleProduct  
(
    BundleId INT,
    ProductId INT
)

INSERT INTO BundleProduct 
VALUES (1, 1), (1, 2), (2, 3), (2, 4)

CREATE TABLE SaleOrder 
(
    OrderId INT,
    OrderNumber NVARCHAR(20)
)

INSERT INTO SaleOrder 
VALUES (1, 'SO0001'), (2, 'SO0002'), (3, 'SO0003')

CREATE TABLE SaleOrderLine 
(
    OrderLineId INT,
    OrderId     INT,
    ProductId   INT
)

INSERT INTO SaleOrderLine 
VALUES (1, 1, 1), (2, 1, 2), (3, 2, 1), 
       (4, 3, 3), (5, 3, 4)

我需要找到客户购买可以组合在一起的产品的订单。例如,在订单SO0001中,产品1和2已售出,此订单必须在结果中。仅在SO0002中出售了产品1。SO0003包含来自Bundle2的产品。这是我需要得到的设定结果:

结果

| OrderId | BundleId |
+---------+----------+
|    1    |     1    |
|    3    |     2    |

如何获得结果?

3 个答案:

答案 0 :(得分:1)

此查询将SaleOrderLines与Bundles结合在一起,以计算特定Bundle中包含的每个SaleOrder中不同的产品。如果此数字是该捆绑销售商品的总数,则我们有一个匹配项:

WITH
  BundleProductCount (BundleID, ProductCount) AS (
    SELECT BundleId, COUNT(ProductId)
    FROM BundleProduct
    GROUP BY BundleId
  ),
  OrderBundleProductCount (OrderId, BundleId, ProductCount) AS (
    SELECT sol.OrderId, bp.BundleId, COUNT(DISTINCT sol.ProductId)
    FROM SaleOrderLine sol
      INNER JOIN BundleProduct bp ON sol.ProductId = bp.ProductId
    GROUP BY sol.OrderId, bp.BundleId
  )
SELECT ob.OrderId, ob.BundleId
FROM OrderBundleProductCount ob
  INNER JOIN BundleProductCount b ON ob.BundleID = b.BundleID
WHERE ob.ProductCount = b.ProductCount;

已添加

您自己想出的一个简短版本,尊重以相同顺序多次出现的产品:

SELECT l.OrderId, bp.BundleId
FROM SaleOrderLine l
  INNER JOIN BundleProduct bp ON l.ProductId = bp.ProductId
GROUP BY l.OrderId, bp.BundleId
HAVING COUNT(DISTINCT l.ProductId) = (
    SELECT COUNT(*)
    FROM BundleProduct
    WHERE BundleId = bp.BundleId
    );

答案 1 :(得分:1)

这是我解决此问题的最佳尝试:

SELECT o.[OrderId], bp.[BundleId]
FROM [SaleOrder] o
INNER JOIN [SaleOrderLine] l ON l.[OrderId] = o.[OrderId]
INNER JOIN [SampleProducts] p ON p.[ProductId] = l.[ProductId]
INNER JOIN [BundleProduct] bp ON bp.[ProductId] = l.[ProductId]
GROUP BY o.[OrderId], bp.[BundleId]
HAVING COUNT(*) = (
    SELECT COUNT(*)
    FROM [Bundle] b
    INNER JOIN [BundleProduct] bp2 ON bp.[BundleId] = b.[BundleId]
    WHERE bp2.[BundleId] = bp.[BundleId]
    GROUP BY b.[BundleId]
    )

首先,我将销售订单行与捆绑产品一起加入,对其进行分组,然后对它们进行计数,然后将其与捆绑中的产品数量进行比较,如果结果相等,则意味着可以创建捆绑。这个想法类似于Wolfgang Kais提出的解决方案,但是没有使用Common Table Expressions

***更新

这是删除不必要的联接的新查询。谢谢@MatBailie

SELECT l.[OrderId], bp.[BundleId]
FROM [SaleOrderLine] l
INNER JOIN [BundleProduct] bp ON bp.[ProductId] = l.[ProductId]
GROUP BY l.[OrderId], bp.[BundleId]
HAVING COUNT(*) = (
    SELECT COUNT(*)
    FROM [BundleProduct] bp2
    WHERE bp2.[BundleId] = bp.[BundleId]
    GROUP BY bp2.[BundleId]
    )

答案 2 :(得分:0)

Row_Number是此处的关键功能。查询可以写为:

select distinct
OrderId , 
BundleId
From  
(
select SO.OrderId,B.BundleId , 
ROW_NUMBER() OVER(PARTITION BY SO.OrderId  ORDER BY SO.OrderId ASC) AS Row#     
from SaleOrderLine SOL
join SaleOrder SO on SOL.OrderId = SO.OrderId
join BundleProduct BP on BP.ProductId = SOL.ProductId
join Bundle B on B.BundleId = BP.BundleId
) As Test

where Row# > 1

Full Query here..