我想根据其中一列的聚合等级选择一组顶行。举个例子,假设我有以下查询:
SELECT CustomerID, ItemID, Price * Qty as Revenue
FROM InvoiceTable
WHERE InvoiceDate between @StartDate, @EndDate
这里非常简单,一个查询返回一组日期之间每张发票的每个行项目。现在,我想要做的是限制这一点,以便只返回属于前十名客户的项目,这些项目基于收入总和。有没有直接的方法来做到这一点?
我总是可以将上述查询的结果转储到临时表中,运行第二遍来计算每个客户的总和并将其存储在临时表的另一列中,然后选择使用RANK OVER子句,但是这似乎有点令人费解,我希望有更直接的方式来实现我的目标。
为了澄清,上面的查询非常简单,在实际生成列表之前预先计算前10个客户的列表是不切实际的(实际查询包含许多联合和其他条件),这就是为什么我要在查询本身中限制它,或者作为后计算步骤。
答案 0 :(得分:2)
你的问题(遗憾地)依赖于db-engine。由于您标记了Transact-SQL,我将首先回答它:
T-SQL具有SELECT TOP N 功能,允许获取第一个 N 行,如果它与ORDER BY子句耦合,则将首先订购,并给你的前N名。如果您使用别名,则Transact还允许按聚合字段进行排序。 你的问题要求两件事情的结合:物品和"顶级买家&#34 ;;所以我们应该首先打击买家,因为他们是过滤器:
SELECT TOP 10 CustomerID, SUM(Price * Qty) as SumRevenue
FROM InvoiceTable
WHERE InvoiceDate between @StartDate, @EndDate
GROUP BY CustomerID
ORDER BY SumRevenue DESC
事情是,你想要这个客户的项目清单。我可以冒昧地猜测你想要每个顾客每件物品的总收入,所以你现在需要用它作为子查询来获得你的物品。我没有进入in vs inner join辩论,所以我只是举个例子:
SELECT r.CustomerID, r.ItemID, SUM(r.Price * r.Qty) ItemRevenue
FROM InvoiceTable
INNER JOIN (SELECT TOP 10 i.CustomerID, SUM(i.Price * i.Qty) as SumRevenue
FROM InvoiceTable i
WHERE i.InvoiceDate between @StartDate, @EndDate
GROUP BY i.CustomerID
ORDER BY SumRevenue DESC) s
ON r.CustomerID = s.CustomerID
WHERE r.InvoiceDate between @StartDate, @EndDate
GROUP BY r.CustomerID, r.ItemID
如果你使用MYSQL,TOP N 成语将被LIMIT N 成语取代,因此会导致:
SELECT r.CustomerID, r.ItemID, SUM(r.Price * r.Qty) ItemRevenue
FROM InvoiceTable
INNER JOIN (SELECT i.CustomerID, SUM(i.Price * i.Qty) as SumRevenue
FROM InvoiceTable i
WHERE i.InvoiceDate between @StartDate, @EndDate
GROUP BY i.CustomerID
ORDER BY SumRevenue DESC
LIMIT 10) s
ON r.CustomerID = s.CustomerID
WHERE r.InvoiceDate between @StartDate, @EndDate
GROUP BY r.CustomerID, r.ItemID
如果您使用Oracle,那么查询会有点困难,因为您没有顶级 N 成语,并且您必须使用ROWNUM和子查询,所以我建议采用这样的方法:
SELECT m.CustomerID, m.ItemID, m.ItemRevenue
(SELECT ROWNUM, r.CustomerID, r.ItemID, SUM(r.Price * r.Qty) ItemRevenue
FROM InvoiceTable
INNER JOIN (SELECT i.CustomerID, SUM(i.Price * i.Qty) as SumRevenue
FROM InvoiceTable i
WHERE i.InvoiceDate between @StartDate, @EndDate
GROUP BY i.CustomerID) s
ON r.CustomerID = s.CustomerID
WHERE r.InvoiceDate between @StartDate, @EndDate
GROUP BY r.CustomerID, r.ItemID
ORDER BY s.SumRevenue DESC) m
WHERE ROWNUM < 11
答案 1 :(得分:1)
根据你有一个复杂的查询这个事实,cte会不会很简单?:
WITH cte
AS ( SELECT TOP 10
CustomerID ,
SUM(Price * Qty) AS TotalRevenue
FROM InvoiceTable
WHERE InvoiceDate BETWEEN @startdate AND @EndDate
GROUP BY CustomerID
ORDER BY Price * Qty DESC
)
SELECT it.CustomerID ,
it.ItemID ,
it.Price * it.Qty AS Revenue
FROM InvoiceTable it
INNER JOIN cte ON it.CustomerID = cte.CustomerID
WHERE it.InvoiceDate BETWEEN @StartDate AND @EndDate
答案 2 :(得分:1)
您可以使用窗口功能执行此操作:
select it.*
from (select it.*, dense_rank() over (order by TotalRevenue) as seqnum
from (select it.*, sum(Price*Qty) over (order by customerid) as TotalRevenue
from InvoiceTable it
where InvoiceDate between @StartDate, @EndDate
) it
) it
where seqnum <= 10;