我需要根据数据创建一个销售摘要,其中包含一个可选的" Elected"标志,由用户控制。该标志确定是否对属于同一类项目的项目进行分组。
如果类中的所有项都没有将Elected标志设置为1,则这些项在摘要中显示为单独的行。如果类中的一个或多个项目的标志设置为1,则该类中的项目将根据设置了标志的第一个项目(最低item_id)进行分组。
例如:
+--------+------+-------+-------+-----+---------+
| ItemId | Item | Class | Price | Qty | Elected |
+--------+------+-------+-------+-----+---------+
| 1 | A1 | 1 | 25.00 | 2 | 1 |
| 2 | A2 | 1 | 20.00 | 3 | 0 |
| 3 | A3 | 1 | 25.00 | 4 | 0 |
| 4 | B1 | 2 | 44.00 | 6 | 0 |
| 5 | B2 | 2 | 41.00 | 7 | 0 |
| 6 | C1 | 3 | 44.00 | 5 | 1 |
| 7 | D1 | 4 | 32.00 | 9 | 0 |
| 8 | D2 | 4 | 30.00 | 8 | 1 |
| 9 | D3 | 4 | 30.00 | 2 | 1 |
+--------+------+-------+-------+-----+---------+
应该提供以下摘要:
+-------+------+--------------+-------------+-----+
| Class | Item | Total Price* | Unit Price^ | Qty |
+-------+------+--------------+-------------+-----+
| 1 | A1 | 70.00 | 35.00 | 2 |
| 2 | B1 | 44.00 | 7.33 | 6 |
| 2 | B2 | 41.00 | 5.85 | 7 |
| 3 | C1 | 44.00 | 8.80 | 5 |
| 4 | D2 | 92.00 | 11.50 | 8 |
+-------+------+--------------+-------------+-----+
* Total Price: SUM() within class
^ Unit Price: Total Price / Qty of elected item
在上表中,第1类通过A1汇总,第3类通过C1汇总,第4类通过D2汇总。第2类根本没有得到总结,而是完全列出。
下面的查询是通过多个自联接进行的黑客攻击,但它可以正常工作。有没有更有效的方法来解决这个问题?
SELECT Sales.Class,
coalesce(SalesWithFlagName.Item, SalesWithoutFlag._Item) 'Item',
SUM(Sales.Price) 'Total Price',
SUM(Sales.Price) / coalesce(SUM(Sales.Qty), SUM(SalesWithoutFlag._Qty)) 'Unit Price', /* Ignore possible Div0 error */
COALESCE(SUM(Sales.Qty), SUM(SalesWithoutFlag._Qty)) 'Qty'
FROM Sales
/* Class with at least one flag set: Get first item with flag */
LEFT JOIN (
SELECT Class _Class,
Elected _Elected,
MIN(ItemId) _ItemId
FROM Sales
GROUP BY Class, Elected
) AS SalesWithFlag ON (
Sales.ItemId = SalesWithFlag._ItemId
AND Sales.Elected = 1
)
/* Get item name from first Left Join */
LEFT JOIN Sales SalesWithFlagName ON (
SalesWithFlag._ItemId IS NOT NULL
AND SalesWithFlag._ItemId = SalesWithFlagName.ItemId
)
/* Class with at least flag not set: Get all items without flag */
LEFT JOIN (
SELECT Class _Class,
ItemId _ItemId,
Item _Item,
Qty _Qty
FROM Sales s2
WHERE NOT EXISTS (SELECT * FROM Sales s3 WHERE s3.Elected = 1 AND s2.Class = s3.Class)
GROUP BY Class, ItemId, Item, Qty
) AS SalesWithoutFlag ON (
Sales.ItemId = SalesWithoutFlag._ItemId
AND Sales.Elected = 0
)
WHERE SalesWithFlag._ItemId IS NOT NULL
OR SalesWithoutFlag._ItemId IS NOT NULL
GROUP BY Sales.Class,
COALESCE(SalesWithFlagName.Item, SalesWithoutFlag._Item)
答案 0 :(得分:0)
您需要将每个类的价格SUM
作为单独的实体,然后将它们连接到条件查询,并对没有标志的类执行相同的操作,而是将它们与选择联合起来语句:
;WITH CTE AS (
SELECT Class, SUM(Price) as [Total Price]
FROM Table1
GROUP BY Class)
, CTE2 AS (
SELECT DISTINCT Class
FROM Table1 AS A
WHERE NOT EXISTS (SELECT DISTINCT Class FROM Table1 AS B WHERE A.Class=B.Class AND Elected = 1))
, CTE3 AS (
SELECT A.Class, A.ItemID, A.Item, B.[Total Price], ROUND(CAST(B.[Total Price] AS FLOAT) / Qty, 2) AS [Unit Price], Qty, ROW_NUMBER() OVER(PARTITION BY A.Class ORDER BY A.ItemID ASC) AS RN
FROM Table1 AS A
JOIN CTE AS B
ON A.Class=B.Class
WHERE A.Elected = 1
UNION ALL
SELECT Class, ItemId, Item, Price, ROUND(CAST(Price AS FLOAT) / Qty, 2), Qty, 1
FROM Table1
WHERE Class IN (SELECT Class FROM CTE2))
SELECT Class, Item, [Total Price], CAST([Unit Price] AS NUMERIC(36,2)) AS [Unit Price], Qty
FROM CTE3
WHERE RN = 1
ORDER BY Class, Item
答案 1 :(得分:0)
您需要单独的行和聚合,因此这是Group Sum
的一项任务和一些额外的逻辑:
WITH cte AS
(
SELECT *,
-- either the individual Price or the Group Sum
CASE
WHEN Elected = 1
THEN SUM(Price) OVER (PARTITION BY Class)
ELSE Price
END AS TotalPrice,
-- is there any Elected = 1 within the Class?
MAX(Elected) OVER (PARTITION BY Class) AS maxElected,
-- Find the first row with Elected = 1
ROW_NUMBER() OVER (PARTITION BY Class ORDER BY Elected DESC, ItemId) AS rn
FROM Sales
)
SELECT
class,
Item,
TotalPrice,
TotalPrice / Qty AS UnitPrice,
QTy
FROM cte
WHERE maxElected = 0 -- no Elected = 1
OR rn = 1 -- first row with Elected = 1
ORDER BY 1,2
;
请参阅Fiddle
答案 2 :(得分:0)
您可以选择每个班级最小项目组合,然后进行简单分组:
;with cte as(select *, (select isnull(min(itemid), t1.itemid)
from t t2
where t2.class = t1.class and t2.elected = 1) as parentid
from t t1)
select p.itemid,
p.item,
p.class,
p.qty,
sum(c.price) as price,
sum(c.price) / p.qty as unitprice
from cte c
join cte p on c.parentid = p.itemid
group by p.itemid, p.item, p.class, p.qty