SQL Server:按分组列求和并按另一列排序

Posted

技术标签:

【中文标题】SQL Server:按分组列求和并按另一列排序【英文标题】:SQL Server: SUM by grouped column and sort by another column 【发布时间】:2019-11-13 22:28:29 【问题描述】:

我想将所有类似 Locations (name) 和 SUM the Volume (vol) 的位置结合起来,并在此处按月份排序结果。我试图选择一个子集并内部加入像这里这样的值,但不断得到分组而不是聚合子句错误,感谢提示! 结果应按月份排序,同一天同一位置的行应汇总在一行而不是多行。 这是我的选择语句,没有分组或求和:

SELECT       
    day_of_month AS 'Day of Month',
    run_ticket   AS 'Ticket number',
    ticketdate   AS 'Ticket Date',
    id           AS 'location id',
    name         AS 'location',
    vol          AS 'Volume',
    ord_rev      AS 'OrderHeader'
FROM 
    #RECORDS R
ORDER BY 
    day_of_month

当前结果: 按月中的某天排序,当天的相同位置不会汇总在一行中。

期望的结果: 按月中的某天排序,当天的相同位置汇总在一行中。 我也在汇总每天的总成交量和运行日期,但在 s-s-rS 中这样做。

我正在尝试 this 这样的解决方案

SELECT       day_of_month       AS 'Day of Month'        
            ,run_ticket         AS 'Ticket Number' 
            ,ticketdate         AS 'Ticket Date'
            ,r2.cmp_id          AS 'Location ID' 
            ,cmp_name           AS 'location'
            ,SUM(vol)           AS 'Volume'
            ,ord_rev            AS 'OrderHeader'


FROM #RECORDS as r2

 JOIN 
 (SELECT cmp_id, SUM(vol) AS 'Volume'
      FROM #RECORDS
      GROUP BY cmp_name
   ) AS s ON s.cmp_id = r2.cmp_id

GROUP BY  r2.cmp_name

ORDER BY day_of_month

当我运行 proc 我得到 ​​p>

消息 8120,级别 16,状态 1,过程 DailyLoadReportMTD,第 78 行 [批处理开始行 109] 列“#RECORDS.cmp_id”在选择列表中无效,因为它既不包含在聚合函数中,也不包含在 GROUP BY 子句中。

好的,Erics 的回答让我如此接近!只需要消除欺骗如果我 GROUP BY r.cmp_name, r.ord_revtype2, r.day_of_month 我必须添加 r.ofr_BSWheight 这会强制重复行...或抛出无效的选择错误 w/o

【问题讨论】:

请向我们展示示例数据和预期结果(作为表格文本)以澄清您的问题。 我在您发布的代码中没有看到,这会导致您提到的错误。 @Eric 感谢更新! 所有那些非聚合列都不在GROUP BY 中。因此错误。 在外部查询中,您按cmp_name 对记录进行分组。那么,如果您有多个记录具有相同的cmp_name,但不同的run_ticketord_rev,会发生什么情况。在这种情况下会选择哪一个? 【参考方案1】:

问题描述引出了“which day_of_month do you want to order by”的问题。

由于您在name 上进行分组以获得vol 的总和,因此可以公平地假设每个name 会有多个R,每个day_of_month 值可能不同.

不会导致“非聚合”错误的可能有效排序表达式是 ORDER BY MIN(day_of_month)ORDER BY MAX(day_of_month).... 您甚至可以使用 AVG 或 SUM,但这些没有多大意义。

另外,' 是字符串分隔符,而不是标识符分隔符。在 MSSQL 中,您使用 ansi 标准 " 或特定于 MS 的 []

【讨论】:

结果应按月份排序,同一天的同一位置应在一行而不是多行上求和。 @RayKoren 听起来你应该按位置和 day_of_month 分组。 抱歉没那么简单【参考方案2】:

由于您想要所有这些列,因此窗口功能可能会更好。这行得通吗?

SELECT       day_of_month       AS 'Day of Month'        
            , run_ticket         AS 'Ticket Number' 
            , ticketdate         AS 'Ticket Date'
            , cmp_id          AS 'Location ID' 
            , cmp_name           AS 'location'
            , SUM(vol) OVER(PARTITION BY day_of_month, cmp_name)           AS 'Volume'
            , ord_rev            AS 'OrderHeader'
FROM #RECORDS

由于您删除了几列,简单的GROUP BY 应该可以工作。

SELECT day_of_month       AS 'Day of Month'        
    , cmp_id          AS 'Location ID' 
    , cmp_name           AS 'location'
    , SUM(vol)           AS 'Volume'
    , ord_rev            AS 'OrderHeader'
FROM #RECORDS
GROUP BY day_of_month, cmp_id, cmp_name, ord_rev

【讨论】:

抱歉,这将返回整个日期范围内该位置的总和。就像每个租约仍然有多条线路,只是每条线路上的整个运行的总和 我在分区中添加了day_of_month 更接近,卷是正确的,只需要以某种方式删除重复的行! 如果run_ticketticketdateord_rev有多个值,你会选择哪一个? 按 ord_rev 删除运行票和票日期和子组 查看更新的问题!谢谢!【参考方案3】:

我使用另一个临时表和 CTE 解决了这个问题

BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

IF OBJECT_ID('tempdb..#RECORDS') IS NOT NULL
BEGIN
    DROP TABLE #RECORDS
END
IF OBJECT_ID('tempdb..#RECORDS2') IS NOT NULL
BEGIN
    DROP TABLE #RECORDS2
END
-- Insert statements for procedure here
CREATE TABLE #RECORDS
(
             day_of_month    int
            --,run_ticket        varchar(255)
            ,inv_seal_ondate datetime   
            ,cmp_id          varchar(255)
            ,cmp_name        varchar(255)   
            ,ofr_BSWHeight   decimal
            ,ord_revtype2    varchar(255)

)
    CREATE TABLE #RECORDS2
(
             day_of_month    int
            --,run_ticket        varchar(255)
            ,inv_seal_ondate datetime   
            ,cmp_id          varchar(255)
            ,cmp_name        varchar(255)   
            ,ofr_BSWHeight   decimal
            ,ord_revtype2    varchar(255)

)

-- Initial population of Records temp table ------------------

INSERT INTO #RECORDS
(            day_of_month
            --,run_ticket        
            ,inv_seal_ondate 
            ,cmp_id          
            ,cmp_name           
            ,ofr_BSWHeight
            ,ord_revtype2   

)                                           

    SELECT  
             (SELECT DAY(inv_seal_ondate)) --day_of_month extracted from inv_seal_ondate
            --,o.run_ticket     
            ,o.inv_seal_ondate   
            ,c.cmp_id           
            ,c.cmp_name         
            ,o.ofr_BSWHeight
            ,oh.ord_revtype2    
    FROM OFR o
    INNER JOIN company c on o.cmp_id = c.cmp_id 
    INNER JOIN orderhead oh on oh.ord_hdrnumber = o.ord_hdrnumber

    WHERE   o.inv_seal_ondate between @StartDate and @EndDate
    AND     c.cmp_altid in (@Company_AltId)

    AND     oh.ord_revtype2 in (@RevType2)

INSERT INTO #RECORDS2
(            day_of_month
            --,run_ticket        
            ,inv_seal_ondate 
            ,cmp_id          
            ,cmp_name           
            ,ofr_BSWHeight
            ,ord_revtype2   

)   
SELECT       
             day_of_month       AS 'Day of Month'
            --,run_ticket       AS 'Run Ticket' 
            ,inv_seal_ondate    AS 'Ticket Date'
            ,cmp_id         AS 'Lease ID' 
            ,cmp_name           AS 'Lease Name'
            ,SUM(ofr_BSWHeight) OVER(PARTITION BY day_of_month  , cmp_name)       AS 'NSV'
            ,ord_revtype2       AS 'OrderHeaderRevType2'


FROM #RECORDS r
ORDER BY day_of_month

;WITH cte AS (
    Select 
                day_of_month        

                ,inv_seal_ondate    

                ,cmp_name           
                ,ofr_BSWHeight      
                ,ord_revtype2       
                ,ROW_NUMBER() OVER (
                    PARTITION BY 
                         day_of_month 
                        ,cmp_name           

            ORDER BY 
                day_of_month
        ) row_num

     FROM 
        #RECORDS2
)
DELETE FROM cte
WHERE row_num > 1;
-------------- Return Primary Result Set ----------------------------

SELECT       
             day_of_month       AS 'Day of Month'
            --,run_ticket           AS 'Run Ticket' 
            ,inv_seal_ondate    AS 'Ticket Date'
            ,cmp_id             AS 'Lease ID' 
            ,cmp_name           AS 'Lease Name'
            ,ofr_BSWHeight      AS 'NSV'
            ,ord_revtype2       AS 'OrderHeaderRevType2'


    FROM #RECORDS2 r2
    ORDER BY day_of_month



END 

【讨论】:

以上是关于SQL Server:按分组列求和并按另一列排序的主要内容,如果未能解决你的问题,请参考以下文章

Python:如何按一列分组行并按另一列选择一行?

按 Sql Server 中的一列分组并按未包含在聚合函数或 GROUP BY 子句中的另一列排序

计算 20 秒间隔内的平均值并按另一列分组

如何按一列的最大值获取SQL行,按另一列分组

SQL查询 - 按另一列排序一组列

熊猫数据框:按列子集+按另一列分组