按总排名返回顶行组

时间:2014-08-12 13:58:49

标签: sql sql-server tsql

我想根据其中一列的聚合等级选择一组顶行。举个例子,假设我有以下查询:

SELECT CustomerID, ItemID, Price * Qty as Revenue
FROM InvoiceTable
WHERE InvoiceDate between @StartDate, @EndDate

这里非常简单,一个查询返回一组日期之间每张发票的每个行项目。现在,我想要做的是限制这一点,以便只返回属于前十名客户的项目,这些项目基于收入总和。有没有直接的方法来做到这一点?

我总是可以将上述查询的结果转储到临时表中,运行第二遍来计算每个客户的总和并将其存储在临时表的另一列中,然后选择使用RANK OVER子句,但是这似乎有点令人费解,我希望有更直接的方式来实现我的目标。

为了澄清,上面的查询非常简单,在实际生成列表之前预先计算前10个客户的列表是不切实际的(实际查询包含许多联合和其他条件),这就是为什么我要在查询本身中限制它,或者作为后计算步骤。

3 个答案:

答案 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;