跨多个表循环遍历所有可能组合的最佳方法?

时间:2013-12-14 16:09:00

标签: sql sql-server coldfusion sql-server-2012

我有两个简单的表格:

OrderStatusOrderStage分别具有OrderStatusIDOrderStageID的主键整数列。每个表返回大约5行。

我要做的是创建一个SELECT语句,它计算每个OrderStatusID和OrderStageID组合的订单数。这是一个例子:

SELECT 
 COUNT(OrderID)
FROM
 Order
WHERE
 OrderStatusID = '1' and OrderStageID = '1'


SELECT 
 COUNT(OrderID)
FROM
 Order
WHERE
 OrderStatusID = '1' and OrderStageID = '2'

......
SELECT 
 COUNT(OrderID)
FROM
 Order
WHERE
 OrderStatusID = '4' and OrderStageID = '5'

我必须编写5x5 SQL语句来计算每种可能组合中的订单数量。如果有人在OrderStatusOrderStage表中添加了更多行,那么我将不得不重新访问此代码以添加新的组合。

此数据的最终呈现将位于网页上的“树”中,与Outlook在邮件面板中显示收件箱,已发送邮件,已删除等电子邮件的计数方式非常相似。我在我的网页上使用ColdFusion。如果我使用GROUP BY语句来获取数据,那么我如何在ColdFusion中单独显示每个结果行? <cfquery group="">会有效吗?

对于给定OrderStatusID和OrderStageID组合的Order表中没有行,它应返回0或NULL。这就是我为每种可能的组合使用单独的SELECT语句的原因。

3 个答案:

答案 0 :(得分:7)

我确信有更简单的方法可以为SQL Server 2012编写它,但基本上可以使用CROSS JOIN来获取状态和阶段(即25行)的所有可能组合。 然后将外部联接返回orders以获取每个组合的计数:

SQL Fiddle

**通常最好避免使用order等关键字作为表名

  SELECT osg.OrderStageID, ost.OrderStatusID, COUNT(o.OrderID) AS TotalOrders
  FROM   orderStage osg CROSS JOIN orderStatus ost
             LEFT JOIN [order] o ON o.OrderStageID = osg.OrderStageID
                    AND o.OrderStatusID = ost.OrderStatusID
  GROUP BY osg.OrderStageID, ost.OrderStatusID

注意:请务必查看CROSS JOIN的运作方式。

答案 1 :(得分:2)

如果要显示不存在的0,可以创建一个包含所有组合的驱动程序表,并加入到该表:

SELECT driver.*,SUM(CASE WHEN o.OrderStatusID IS NOT NULL THEN 1 ELSE 0 END)AS OrderCount
FROM (SELECT *
      FROM  (SELECT DISTINCT OrderStatusID FROM Orders) a
           ,(SELECT DISTINCT OrderStageID FROM Orders) b
      ) driver
LEFT JOIN Orders o
 ON driver.OrderStatusID  = o.OrderStatusID
 AND driver.OrderStageID = o.OrderStageID
GROUP BY driver.OrderStatusID
       , driver.OrderStageID

演示:SQL Fiddle

答案 2 :(得分:1)

在回答这些类型的问题时,我喜欢在tempdb中创建一个示例表。

--
--  Create test table
--

-- Just toss away code
USE tempdb;
GO

-- Create a simple table
CREATE TABLE my_orders
(
  my_orderstatus_id int,
  my_orderstage_id int
);
GO

如果你没有看过Jeff Moden的计数表,你应该这样做。它们是以关系代数方式进行循环的快速方法。我正在加载组合(1..5)(1..5)次数和(6..10)(6..10)两次。 go之后的数字告诉我们运行TSQL的次数。

--
--  Load test table
--

-- Create some data (1-5) x 4 counts
;
WITH cteTally1 (my_number1) AS 
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY a.name) AS my_number
    FROM 
        sys.objects a 
    CROSS JOIN 
        sys.objects b
),
cteTally2 (my_number2) AS 
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY a.name) AS my_number
    FROM 
        sys.objects a 
    CROSS JOIN 
        sys.objects b
)
INSERT INTO my_orders 
SELECT my_number1, my_number2 
FROM cteTally1 T1 CROSS JOIN cteTally2 T2
WHERE 
    (T1.my_number1 > 0 AND T1.my_number1 <= 5 ) AND 
    (T2.my_number2 > 0 AND T2.my_number2 <= 5 );
GO 4


-- Create some data (6-10) x 2 counts
;
WITH cteTally1 (my_number1) AS 
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY a.name) AS my_number
    FROM 
        sys.objects a 
    CROSS JOIN 
        sys.objects b
),
cteTally2 (my_number2) AS 
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY a.name) AS my_number
    FROM 
        sys.objects a 
    CROSS JOIN 
        sys.objects b
)
INSERT INTO my_orders 
SELECT my_number1, my_number2 
FROM cteTally1 T1 CROSS JOIN cteTally2 T2
WHERE 
    (T1.my_number1 >= 6 AND T1.my_number1 <= 10 ) AND 
    (T2.my_number2 >= 6 AND T2.my_number2 <= 10 );
GO 2

-- Show the data
SELECT * FROM my_orders;

最后但并非最不重要的是,一个简单的分组将为您提供每个组合的计数。

--
-- Simple group by will suffice
--

SELECT 
    my_orderstatus_id, 
    my_orderstage_id,
    COUNT(*) as number  
FROM my_orders
GROUP BY 
    my_orderstatus_id, 
    my_orderstage_id;
GO

输出的快速屏幕截图:

enter image description here

这应该可以解答您的特定问题。但如果我确实错误地提出要求,请回复。

您现在更改了初始要求。我们在管理层中称之为 SCOPE CHANGE

如果您知道组合总是10 x 10,请创建矩阵表或公用表表达式,这样无论匹配如何,您都将始终拥有组合。

通过相应的方式将简单组扩展为左连接到数据和组/顺序。

--
-- Report Query
--

-- For missing records
;
WITH cteTally1 (my_number1) AS 
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY a.name) AS my_number
    FROM 
        sys.objects a 
    CROSS JOIN 
        sys.objects b
),
cteTally2 (my_number2) AS 
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY a.name) AS my_number
    FROM 
        sys.objects a 
    CROSS JOIN 
        sys.objects b
),
cteMissingCombos (my_orderstatus_id, my_orderstage_id) AS 
(
    SELECT T1.my_number1, T2.my_number2
    FROM cteTally1 T1 CROSS JOIN cteTally2 T2
    WHERE T1.my_number1 < 11 AND T2.my_number2 < 11
)

-- Return null if no records, use coalesce if you want 0
SELECT 
    C.my_orderstatus_id, 
    C.my_orderstage_id,
    COUNT(O.my_orderstatus_id) as number  
FROM 
    cteMissingCombos as C LEFT JOIN my_orders as O
ON 
    C.my_orderstatus_id = O.my_orderstatus_id AND 
    C.my_orderstage_id = O.my_orderstage_id
GROUP BY 
    C.my_orderstatus_id,
    C.my_orderstage_id
ORDER BY 
    C.my_orderstatus_id,
    C.my_orderstage_id
GO

下面的输出显示我们没有1 x 6到10的组合。因此

enter image description here