t-SQL 查找每个组的前 10 条记录

Posted

技术标签:

【中文标题】t-SQL 查找每个组的前 10 条记录【英文标题】:t-SQL to find Top 10 Records for Each Group 【发布时间】:2013-04-24 13:50:57 【问题描述】:

我试图弄清楚如何返回每组 Trans.TranSID 的前 10 条记录。

SELECT a.ABID, a.ABName, t.TranSID, SUM(IIF(TranTypeID = 'CO', td.Qty * CAST(td.Price AS money) * - 1, 
                      td.Qty * CAST(td.Price AS money))) AS TotalSales
FROM         Trans t INNER JOIN
                      TransDetail td ON t.TranID = td.TranID INNER JOIN
                      ABook a ON t.TranABID = a.ABID
WHERE     (t.TranDate BETWEEN CONVERT(DATETIME, '2012-01-01 00:00:00', 102) AND CONVERT(DATETIME, '2013-01-01 00:00:00', 102)) AND 
           t.TranTypeID in ('SO','CA','CO') AND (t.TranStatus <> 'V')
GROUP BY a.ABID, a.ABName, t.TranSID
HAVING  (NOT (a.ABName LIKE '%cash%'))
ORDER BY t.TranSID, TotalSales Desc

我可以在 select 语句中添加“前 10 个”,但这会为我提供前 10 个帐户,而与组无关。 Trans.TranSID 有 25 组,我试图只为每组获得前 10 名。

【问题讨论】:

包括一个基本的表结构和预期的输出将帮助你得到答案。 基本上有客户账户表(Abook)和交易(Trans)和交易明细(TransDetail)表。非常简单的结构。只需对每个客户的销售额求和并将前 10 个结果分组为 TranSID(即交易发生的位置)。 【参考方案1】:

我认为您正在寻找带有PARTITION BYROW_NUMBER()

SELECT * 
FROM (
    SELECT 
        ROW_NUMBER() OVER(PARTITION BY t.TranSID ORDER BY t.TranSID, SUM(IIF(TranTypeID = 'CO', td.Qty * CAST(td.Price AS money) * - 1, td.Qty * CAST(td.Price AS money))) DESC) as RowNum,            
        a.ABID, 
        a.ABName, 
        t.TranSID, 
        SUM(IIF(TranTypeID = 'CO', td.Qty * CAST(td.Price AS money) * - 1, td.Qty * CAST(td.Price AS money))) AS TotalSales
    FROM Trans t 
       INNER JOIN TransDetail td 
           ON t.TranID = td.TranID 
       INNER JOIN ABook a 
           ON t.TranABID = a.ABID
    WHERE (t.TranDate BETWEEN CONVERT(DATETIME, '2012-01-01 00:00:00', 102) AND CONVERT(DATETIME, '2013-01-01 00:00:00', 102)) 
       AND t.TranTypeID in ('SO','CA','CO')
       AND (t.TranStatus <> 'V')
    GROUP BY a.ABID, a.ABName, t.TranSID
    HAVING  (NOT (a.ABName LIKE '%cash%'))
) a
WHERE a.RowNum <=10

这将为分组中的每条记录分配一个行号(由PARTITION定义的列,从1到n。从那里,您可以在其上运行SELECT以获取任意数量的记录组。

【讨论】:

我在运行该代码时收到以下错误消息。消息 1033,级别 15,状态 1,第 20 行 ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效,除非还指定了 TOP、OFFSET 或 FOR XML。 @GaryGerson,我已经更新了我的脚本。尝试删除子查询底部的ORDER BY 是的,明白了。我实际上是和你同时解决的。【参考方案2】:

我对 t-sql 不是特别熟悉,不幸的是,我无法访问 t-sql 数据库来测试这是否可以实现您的目标。

也就是说,我认为这是您可以使用子查询和 ROW_NUMBER 函数来完成它的一种方法。

SELECT
    *
FROM (
    SELECT 
        a.ABID
        , a.ABName
        , t.TranSID
        , SUM(IFF(TranTypeID = 'CO'
            , td.Qty * CAST(td.Price AS MONEY) * -1
            , td.Qty * CAST(td.Price AS MONEY))) AS TotalSales
        , ROW_NUMBER() 
            OVER(PARTITION BY t.TranSID 
                ORDER BY SUM(IFF(TranTypeID = 'CO'
                    , td.Qty * CAST(td.Price AS MONEY) * -1
                    , td.Qty * CAST(td.Price AS MONEY))) DESC) AS row
    FROM
        Trans t 
        INNER JOIN TransDetail td 
        ON t.TranID = td.TranID 

        INNER JOIN ABook a 
        ON t.TranABID = a.ABID

    WHERE
        (t.TranDate BETWEEN CONVERT(DATETIME, '2012-01-01 00:00:00', 102) AND CONVERT(DATETIME, '2013-01-01 00:00:00', 102))
        AND t.TranTypeID in ('SO','CA','CO') 
        AND (t.TranStatus <> 'V')

    GROUP BY 
        a.ABID
        , a.ABName
        , t.TranSID

    HAVING
        (NOT (a.ABName LIKE '%cash%'))
) q

WHERE
    q.row <= 10

【讨论】:

同上,运行时出现以下错误信息。消息 1033,级别 15,状态 1,第 20 行 ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效,除非还指定了 TOP、OFFSET 或 FOR XML。 我相信您的第一个版本的问题是 ROW_NUMBER() 字段在 sum 函数中有“AS TotalSales”。你在那里的东西现在可以工作了。

以上是关于t-SQL 查找每个组的前 10 条记录的主要内容,如果未能解决你的问题,请参考以下文章

SQL Query获取组的前2条记录

mysql中分组之后取每个组的前三个

获取每个组的最新 n 条记录

Oracle分组查询取每组排序后的前N条记录

您如何检索每个分组中的前两条记录

SQL:查找每组的最大记录[重复]