连接具有空值的汇总数据 - SQL Server

Posted

技术标签:

【中文标题】连接具有空值的汇总数据 - SQL Server【英文标题】:Joining Summed data that has nulls - SQL Server 【发布时间】:2013-08-14 03:44:55 【问题描述】:

在通过Amount 列对下面的数据求和并通过Line_Num 字段分组时,如何在适当的地方保留Acct_Name 字段?在添加帐户名称时,Line_Num 列中的“Null”值会导致分组术语出现问题。帐户 C 和 D 在 Line_Num 中都有 Null 值。如果我将Acct_Name 添加到group by 子句中,我将失去仅通过Line_Num 字段对值求和的能力。

我正在尝试根据行号汇总会计行和组。空数据不是我做的,可惜只是我手上的数据集。

原始数据:

Acct_Name   ID      Line_Num   Amount
Acct A      1       1_01       100.0000
Acct A      1       1_01       -50.0000
Acct A      1       1_02       75.0000
Acct A      1       _02        125.0000
Acct B      2       2_01       200.0000
Acct B      2       2_01       50.0000
Acct B      2       2_02       25.0000
Acct C      3       3_01       75.0000
Acct C      3       3_02       50.0000
Acct C      3       3_03       -25.0000
Acct C      3       Null       65.0000
Acct D      4       Null       300.0000
Acct D      4       _02        100.0000
Acct D      4       Null       -50.0000
Acct D      4       Null       75.0000

如果Line_Num 值为空,则允许该行与其他空值聚合。它会在报告中显示为下落不明,并且可以得到适当的处理。

理想的处理数据集:

Amount  Line_Num Acct_Name
390.00  Null     Null
225.00  _02      Null
50.00   1_01     Acct A
75.00   1_02     Acct A
250.00  2_01     Acct B
25.00   2_02     Acct B
75.00   3_01     Acct C
50.00   3_02     Acct C
-25.00  3_03     Acct C

以下是我使用过的查询:

Select SUM(Amount), Line_Num
FROM dbo.tblRawData
Group By Line_Num

此查询运行良好,但它不包含任何聚合字段中的帐户名称。我需要不包含空值的字段中的帐户名称。

Select SUM(Amount), Line_Num, Acct_Name
FROM dbo.tblRawData
Group By Line_Num, Acct_Name

此查询包含帐户名称,但它最终会根据帐户名称进行分组,而不仅仅是 Line_Num。

Select *
From dbo.tblRawData a
Inner Join dbo.tblRawData b On (a.Line_Num = b.Line_Num)
(SELECT SUM(CAST(Amount as money)) as Amount, Line_Num
FROM dbo.tblRawData
GROUP BY Line_Num)

此内部连接旨在仅连接在 Line Num 上等效的那些行,但我收到的是笛卡尔结果集。显然我没有正确编写此连接,或者我使用了不正确的命令。

这是可用于构建我正在使用的相同架构的查询:

CREATE TABLE [dbo].[tblRawData](
[Acct_Name] [nvarchar](50) NULL,
[ID] [nvarchar](50) NULL,
[Line_Num] [nvarchar] (50),
[Amount] [money]
) ON [PRIMARY]

GO

insert into dbo.tblRawData values ('Acct A', '1', '1_01', '100')
insert into dbo.tblRawData values ('Acct A', '1', '1_01', '-50')
insert into dbo.tblRawData values ('Acct A', '1', '1_02', '75')
insert into dbo.tblRawData values ('Acct A', '1', '_02', '125')
insert into dbo.tblRawData values ('Acct B', '2', '2_01', '200')
insert into dbo.tblRawData values ('Acct B', '2', '2_01', '50')
insert into dbo.tblRawData values ('Acct B', '2', '2_02', '25')
insert into dbo.tblRawData values ('Acct C', '3', '3_01', '75')
insert into dbo.tblRawData values ('Acct C', '3', '3_02', '50')
insert into dbo.tblRawData values ('Acct C', '3', '3_03', '-25')
insert into dbo.tblRawData values ('Acct C', '3', '', '65')
insert into dbo.tblRawData values ('Acct D', '4', '', '300')
insert into dbo.tblRawData values ('Acct D', '4', '_02', '100')
insert into dbo.tblRawData values ('Acct D', '4', '', '-50')
insert into dbo.tblRawData values ('Acct D', '4', '', '75')

附: SQL Fiddle 目前似乎无法访问(可能在我这边,不知道)

编辑

看看下面的代码,如果在尝试实现我的目标时似乎存在明显的缺陷,请大笑。如果Line_Item 不匹配,我宁愿Acct_Name 保持为空,但也许我可以解决这个问题。

IF (SELECT object_id('TempDB..#temp4')) IS NOT NULL
BEGIN
    DROP TABLE #temp4
END

SELECT SUM(CAST(Amount as money)) as Amount, Line_Num INTO #temp4
FROM dbo.tblRawData
GROUP BY Line_Num

Select * from #temp4

Select MAX(a.Acct_Name) as Acct_Name, MAX(b.Line_Num) as Line_Num, MAX(b.Amount) as Amount
From dbo.tblRawData a
Inner Join #temp4 b On (a.Line_Num = b.Line_Num)
Group By b.Line_Num

结果:

Acct_Name  Line_Num Amount
Acct D     Null     390.00
Acct D     _02      225.00
Acct A     1_01     50.00
Acct A     1_02     75.00
Acct B     2_01     250.00
Acct B     2_02     25.00
Acct C     3_01     75.00
Acct C     3_02     50.00
Acct C     3_03     -25.00

【问题讨论】:

【参考方案1】:

给你:

;WITH CTE AS
(
    SELECT  Line_Num, 
            SUM(Amount) Amount,
            MIN(Acct_Name) MinAcct_Name,
            MAX(Acct_Name) MaxAcct_Name
    FROM tblRawData
    GROUP BY Line_Num
)
SELECT  Amount,
        Line_Num,
        CASE WHEN Line_Num IS NULL 
        OR MinAcct_Name <> MaxAcct_Name THEN NULL
        ELSE MinAcct_Name END Acct_Name
FROM CTE

【讨论】:

很有趣。使用 AVG(Amount) AvgAmount, COUNT(Amount) CntAmount 而不是 SUM(Amount)SELECT AvgAmount * CntAmount as Amount, etc... 而不是 SELECT Amount, etc... 这将是一个有趣的罕见查询,同时使用四个最有用的聚合函数 :) 但是,当然,它不是最优的。 谢谢拉马克!我在 CTE 中添加了额外的 MAX(ColumnName) 并收集了其余的公共数据(示例中不包括实时数据)。像冠军一样工作!

以上是关于连接具有空值的汇总数据 - SQL Server的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server的空值处理策略

SQL从具有空值的行数据中选择MIN值

需要为每条记录查找具有空值的所有列 - 访问 SQL 或 vba

访问不允许SQL-Server列中的空值的INSERT或UPDATE(访问运行时错误3162)

具有空值的完全外连接自身

如何获取sql中给定行具有空值的列数?