英文:
Trying to Collapse Multiple Rows to Single Result
问题
以下是您要翻译的内容:
我的源数据的一个简单示例:
OrderStatus | CustomerID | OrderNbr | LoadNbr | Product | Quantity
------------+------------+----------+---------+---------+---------
OPEN | 1 | ORD00001 | 1 | 0012 | 12
OPEN | 1 | ORD00001 | 2 | 0024 | 20
我试图在结果中实现的目标:
OrderStatus | CustomerID | OrderNbr | Prod01 | Quantity01 | Prod02 | Quantity02 | Ratio01 | Ratio02
-------------+------------+----------+--------+------------+--------+------------+---------+---------
OPEN | 1 | ORD00001 | 0012 | 12 | 0024 | 20 | 37.5 | 62.5
为了简洁起见,我只包含了2种产品。但是在给定的数据中,可以有多达8种产品。
我尝试过的方法:
使用 OrderSummary AS (
SELECT O.OrderStatus,
O.CustomerID,
O.OrderNbr,
'TotalQty' = I1.Quantity + I2.Quantity,
'Prod01' = I1.Product,
'Quantity01' = I1.Quantity,
'Prod02' = I2.Product,
'Quantity02' = I2.Quantity
FROM Orders O
LEFT JOIN Orders I1 ON O.OrderNbr = I1.OrderNbr AND I1.LoadNbr = 1
LEFT JOIN Orders I2 ON O.OrderNbr = I2.OrderNbr AND I1.LoadNbr = 2
)
SELECT *,
'Ratio01' = Quantity01 / TotalQty * 100,
'Ratio02' = Quantity02 / TotalQty * 100
FROM OrderSummary
这基本上是我目前正在运行的内容。但是我获得的结果看起来像这样:
OrderStatus | CustomerID | OrderNbr | Prod01 | Quantity01 | Prod02 | Quantity02 | Ratio01 | Ratio02
-------------+------------+----------+--------+------------+--------+------------+---------+---------
OPEN | 1 | ORD00001 | 0012 | 12 | 0024 | 20 | 37.5 | 62.5
OPEN | 1 | ORD00001 | 0012 | 12 | 0024 | 20 | 37.5 | 62.5
因此,我尝试过使用 `DISTINCT` 声明,这在大多数情况下可以工作,但似乎有点笨拙。
**所以我要问的问题是**,我是否以正确的方式解决了这个问题。在相同表上使用连接来将结果合并为一行是最佳方法吗?还是有更好的方法可以实现这一目标?我在工作环境中受到一定限制:我无法控制提供给我的数据格式,我*必须*将结果以单行形式呈现给执行数据库查询的应用程序。
编辑:进一步澄清。对于任何给定的订单,状态、客户、订单编号等都将相同。加载号、产品和数量行在每个条目之间都是唯一的。因此,具有8种产品的订单将有8行,我试图将其合并为一行。
请注意,我已经删除了您原始内容中的 HTML 编码以使其更易读。如果您需要任何进一步的帮助,请告诉我。
英文:
A rough example of my source data:
OrderStatus | CustomerID | OrderNbr | LoadNbr | Product | Quantity
------------+------------+----------+---------+---------+---------
OPEN | 1 | ORD00001 | 1 | 0012 | 12
OPEN | 1 | ORD00001 | 2 | 0024 | 20
And what I'm trying to achieve in a result:
OrderStatus | CustomerID | OrderNbr | Prod01 | Quantity01 | Prod02 | Quantity02 | Ratio01 | Ratio02
-------------+------------+----------+--------+------------+--------+------------+---------+---------
OPEN | 1 | ORD00001 | 0012 | 12 | 0024 | 20 | 37.5 | 62.5
For the sake of brevity I only included 2 products. But in the data I've been given there can be upwards of 8 products.
What I've been trying:
WITH OrderSummary AS (
SELECT O.OrderStatus,
O.CustomerID,
O.OrderNbr,
'TotalQty' = I1.Quantity + I2.Quantity,
'Prod01' = I1.Product,
'Quantity01' = I1.Quantity,
'Prod02' = I2.Product,
'Quantity02' = I2.Quantity
FROM Orders O
LEFT JOIN Orders I1 ON O.OrderNbr = I1.OrderNbr AND I1.LoadNbr = 1
LEFT JOIN Orders I2 ON O.OrderNbr = I2.OrderNbr AND I1.LoadNbr = 2
)
SELECT *,
'Ratio01' = Quantity01 / TotalQty * 100,
'Ratio02' = Quantity02 / TotalQty * 100
FROM OrderSummary
Which is more or less what I've currently got running. But the results I do get look like this:
OrderStatus | CustomerID | OrderNbr | Prod01 | Quantity01 | Prod02 | Quantity02 | Ratio01 | Ratio02
-------------+------------+----------+--------+------------+--------+------------+---------+---------
OPEN | 1 | ORD00001 | 0012 | 12 | 0024 | 20 | 37.5 | 62.5
OPEN | 1 | ORD00001 | 0012 | 12 | 0024 | 20 | 37.5 | 62.5
So I just tried running with a DISTINCT
declaration which works most of the time but seems clunky.
So the question I have is whether I'm approaching this problem from the right angle or not. Is using joins on the same table the best way to collapse the results to a single row? Or is there a better way to achieve this? I am somewhat constrained by the environment I'm working in: I have no control over the data format given to me and I must present the results in a single row to the application executing the database query.
EDIT: Some additional clarification. For any given order the Status, Customer, OrderNumber, etc. would all be the same. The LoadNbr, Product, and Quantity rows would be unique between each entry. So an Order with 8 products would have 8 rows I'm trying to collapse to a single row.
Late Edit / Clarification:
In my question I had mentioned I get duplicate rows no matter what. That was a mistake. I found data in my production data that was not identical (some dates with different values by milliseconds). I've removed that section for anyone finding this question.
答案1
得分: 2
以下是您提供的代码的翻译部分:
首先,您提到的第一种方法是使用"条件聚合"而不是"旋转操作符"来实现的。但是,如果您在现有查询中添加了一个WHERE子句来抑制不需要的行,您提供的查询将起作用,示例如下:
-- 代码示例
-- ...
但是这种方法非常不优雅并且存在问题(例如,如果任何数量缺失,TotalQty可能为NULL)。
另一种方法是使用以下方法:
-- 代码示例
-- ...
这种方法也可以做得"动态",如下所示:
-- 代码示例
-- ...
您可以在此演示中查看示例。
英文:
In my view the simplest way to do this is through "conditional aggregates" and not via the "pivot operator". However first your existing query would work IF you include a where clause to suppress the unwanted rows: e.g:
WITH OrderSummary
AS (
SELECT
O.OrderStatus
, O.CustomerID
, O.OrderNbr
, 'TotalQty' = O.Quantity + I2.Quantity + I3.Quantity + I4.Quantity
+ I5.Quantity + I6.Quantity + I7.Quantity + I8.Quantity
, 'Prod01' = O.Product
, 'Quantity01' = O.Quantity
, 'Prod02' = I2.Product
, 'Quantity02' = I2.Quantity
, 'Prod03' = I3.Product
, 'Quantity03' = I3.Quantity
-- more of the same here
FROM Orders O
LEFT JOIN Orders I2 ON O.OrderNbr = I2.OrderNbr AND I2.LoadNbr = 2
LEFT JOIN Orders I3 ON O.OrderNbr = I3.OrderNbr AND I3.LoadNbr = 3
LEFT JOIN Orders I4 ON O.OrderNbr = I4.OrderNbr AND I4.LoadNbr = 4
-- more of the same here
WHERE O.LoadNbr = 1
)
SELECT
*
, 'Ratio01' = Quantity01 / TotalQty * 100
, 'Ratio02' = Quantity02 / TotalQty * 100
, 'Ratio03' = Quantity03 / TotalQty * 100
-- more of the same here
FROM OrderSummary
but this is so very ugly and has problems (e.g. TotalQty could be NULL if any of the quantities are missing.
An alternative is to use an approach like this:
SELECT
OrderStatus
, CustomerID
, OrderNbr
, ca.TotalQty
, max(CASE WHEN o.LoadNbr = 1 THEN o.Product ELSE '' END) AS Product1
, max(CASE WHEN o.LoadNbr = 1 THEN o.Quantity ELSE 0 END) AS Quantity1
, max(CASE WHEN o.LoadNbr = 1 THEN round(o.Quantity * 100.0 / ca.TotalQty,2) ELSE 0 END) AS Ratio1
, max(CASE WHEN o.LoadNbr = 2 THEN o.Product ELSE '' END) AS Product2
, max(CASE WHEN o.LoadNbr = 2 THEN o.Quantity ELSE 0 END) AS Quantity2
, max(CASE WHEN o.LoadNbr = 2 THEN round(o.Quantity * 100.0 / ca.TotalQty,2) ELSE 0 END) AS Ratio2
-- more of the same here
FROM Orders AS O
CROSS APPLY (
SELECT SUM(q.Quantity) AS TotalQty
FROM Orders AS q
WHERE q.OrderNbr = o.OrderNbr
) AS ca
GROUP BY
OrderStatus
, CustomerID
, OrderNbr
and this can be made "dynamic" as well, like so:
DECLARE @cols AS NVARCHAR(MAX);
DECLARE @sql AS NVARCHAR(MAX);
SET @cols = STUFF((SELECT ', max(CASE WHEN o.LoadNbr = '
+ CONVERT(VARCHAR(10), LoadNbr) + ' THEN o.Product ELSE '''' END) AS Product'
+ CONVERT(VARCHAR(10), LoadNbr) + ', max(CASE WHEN o.LoadNbr = '
+ CONVERT(VARCHAR(10), LoadNbr) + ' THEN o.Quantity ELSE 0 END) AS Quantity'
+ CONVERT(VARCHAR(10), LoadNbr) + ', max(CASE WHEN o.LoadNbr = '
+ CONVERT(VARCHAR(10), LoadNbr) + ' THEN round(o.Quantity * 100.0 / ca.TotalQty,2) ELSE 0 END) AS Ratio'
+ CONVERT(VARCHAR(10), LoadNbr)
FROM (SELECT DISTINCT LoadNbr FROM Orders) O
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,2,'');
select @cols;
SET @sql = 'SELECT OrderStatus
, CustomerID
, OrderNbr
, ca.TotalQty
, ' + @cols + '
FROM Orders AS O
CROSS APPLY (
SELECT SUM(q.Quantity) AS TotalQty FROM Orders AS q
WHERE q.OrderNbr = o.OrderNbr
) AS ca
GROUP BY OrderStatus
, CustomerID
, OrderNbr
, ca.TotalQty';
select @sql;
EXEC sp_executesql @sql;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论