如何动态计算 GROUP 中多列的总和?
Posted
技术标签:
【中文标题】如何动态计算 GROUP 中多列的总和?【英文标题】:How to dynamically calculate the sums of many columns in a GROUP? 【发布时间】:2018-01-04 12:22:39 【问题描述】:在下表中,我有可变数量的列,该数字在 1000 秒以内。我需要对按人名分组的 1000 列中的每一列的所有值求和。所以,smith's
总计test_score_1
,总计test_score_2
,...总计test_score_1000
。然后Jackson's
总计test_score_1
,总计test_score_2
,...总计test_score_1000
。
我事先不知道“test_score_n
”列的数量,它们总是在变化。
所以给定这张表:
name test_score_1 test_score_2 ... test_score_1000
smith 2 1 0
jackson 0 3 1
jackson 1 1 2
jackson 3 0 3
smith 4 5 1
如何制作下表?
name test_score_1 test_score_2 ... test_score_1000
smith 6 6 1
jackson 4 4 6
【问题讨论】:
你有 1000 列?!第一项任务:立即修复这种疯狂。 生物数据很容易拥有超过 1024 列。有人可能会争辩说,不应该在数据库管理系统中进行数据转换,但是在 R 中处理大于 ram 的数据是很痛苦的,主要是因为我需要的算法不需要部分学习。我更喜欢在 sqlserver 中生成数据集,并在数据科学部分使用 R 或 python。 我仍然不相信 - 我仍然会将数据标准化为多个表。另外,我认为如果您仍然绝对需要 1000 列,那么 SQL 数据库可能不是存储数据的好地方。 【参考方案1】:演示: http://rextester.com/MAFCP19297
SQL
DECLARE @cols varchar(max), @sql varchar(max);
SELECT @cols =
COALESCE(@cols + ', ', '') + 'SUM(' + COLUMN_NAME + ') AS ' + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = '<tbl name>'
AND COLUMN_NAME <> 'name'
-- The AND below may be optional - see "Additional Notes #1"
AND TABLE_CATALOG = '<database schema name>';
SET @sql = 'SELECT name, ' + @cols + ' FROM tbl GROUP BY name;';
EXEC (@sql);
说明
DECLARE
创建两个变量 - 一个用于存储 SQL 的列求和部分,另一个用于存储要运行的整个动态创建的 SQL 语句。
SELECT
查询INFORMATION_SCHEMA.COLUMNS
系统表,获取tbl
中除name
列之外的所有列的名称。 (也可以使用 sys 表 - 对this question 的回答讨论每个表的相对优点)。然后使用this method(可以说比替代的FOR XML PATH ('')
方法简单一点)将这些行值转换为单个逗号分隔值。逗号分隔的值不仅仅是列名 - 它们SUM
覆盖每个列名,然后为结果分配同名的别名。
SET
然后构建一个简单的 SQL 语句,选择名称和所有求和值 - 例如:SELECT name, SUM(test_score_1) AS test_score_1, SUM(test_score_2) AS test_score_2, SUM(test_score_1000) AS test_score_1000 FROM tbl GROUP BY name;
。
EXEC
然后运行上述查询。
附加说明
-
如果表名在所有数据库中可能不是唯一的,则选择中需要以下子句:
AND TABLE_CATALOG = '<database schema name>
'
我对这个问题的最初回答是错误地使用 mysql 而不是 SQL Server - 现在已更正,但以前的版本仍在编辑历史中,可能对某人有所帮助...
【讨论】:
非常感谢您的解释,帮了大忙。甚至还有一个演示。【参考方案2】:生成 SQL 的 SQL
DECLARE @generatedSQL nvarchar(max);
SET @generatedSQL = (
SELECT
'SELECT ' +
SUBSTRING(X.foo, 2, 2000) +
'FROM ' +
QUOTENAME(SCHEMA_NAME(t.schema_id)) + '.' + QUOTENAME(t.name) +
' GROUP BY name' --fix this line , edited
FROM
sys.tables t
CROSS APPLY
(
SELECT
', SUM(' + QUOTENAME(c.name) + ')'
FROM
sys.columns c
WHERE
c.object_id = t.object_id
AND
c.name <> 'Name'
FOR XML PATH('')
) X (foo)
WHERE
t.name = 'MyTable'
);
EXEC (@generatedSQL);
【讨论】:
嗨,选择“选择”是什么意思?我可以在这段代码中添加一个 group by 子句吗?我想按名称分组(请参阅修改后的问题),然后总结每个名称的 1000 列。谢谢! @gbn - 我认为您需要从CROSS APPLY
中排除 name
列
@user798719 - 我建议你去谷歌看看“什么是动态 SQL”
@Alex 你为什么这么说?我需要它来生成 CSV
@SteveChambers 谢谢,知道了。,该组是后来添加的【参考方案3】:
用你的表名更改tablename
。
Declare @query as nvarchar(MAX) = (SELECT
'SELECT name,' + SUBSTRING(tbl.col, 2, 2000) + ' FROM ' + QUOTENAME(SCHEMA_NAME(t.schema_id)) + '.' + QUOTENAME(t.name) + 'Group By name'
FROM
sys.tables t
CROSS APPLY
(
SELECT
', SUM(' + QUOTENAME(columns.name) + ') as ' + columns.name
FROM
sys.columns columns
WHERE
columns.object_id = t.object_id and columns.name != 'name'
FOR XML PATH('')
) tbl (col)
WHERE
t.name = 'tablename')
select @query EXECUTE(@query)
【讨论】:
这个答案似乎很受@gbn的回答的启发 SELECT ', SUM(' + QUOTENAME(columns.name) + ') as ' + columns.name 我无法理解代码。您如何在每列中插入“总和”,而不是对所有列求和一次?在哪里添加 group by 子句?我想在对 10000 个动态列中的每一个求和之前按“名称”进行分组。 我在使用您的代码时出错:“SELECT id, SUM([003]) as 003, SUM([008]) as 008, SUM([009]) as 009, SUM([ 010]) as 010, SUM([011]) ... SUM( FROM [dbo].[t1]Group By name" 我使用了一个小样本,它有 362 列(003、008、009 等是列名)。生成的 select 语句没有显示所有列,切断了很多。 @user798719 请发送完整的查询和错误详细信息。在我的数据库中同样工作。【参考方案4】:试试下面的脚本
(将@tableName= [yourTablename] 和@nameColumn 设置为您要分组的字段的名称)
Declare @tableName varchar(50)='totalscores'
Declare @nameColumn nvarchar(50)='name'
Declare @query as nvarchar(MAX) ;
select @query = 'select ' + nameColumn + cast(sumColumns as nvarchar(max)) + 'from ' + @tableName +' group by ' + nameColumn from (
select @nameColumn nameColumn, (SELECT
', SUM(' + QUOTENAME(c.name) + ') ' + QUOTENAME(c.name)
FROM
sys.columns c
WHERE
c.object_id=t.object_id and c.name != @nameColumn
order by c.name
FOR
XML path(''), type
) sumColumns
from sys.tables t where t.name= @tableName
)t
EXECUTE(@query)
【讨论】:
【参考方案5】:GBN 的动态 SQL 将是我的首选 (+1),而且性能会更高。 但是,如果您有兴趣打破 1,000 多列的可怕循环,请考虑以下事项:
示例
Declare @YourTable Table ([col 1] int,[col 2] int,[col 1000] varchar(50))
Insert Into @YourTable Values
(2,1,0)
,(4,5,1)
Select Item = replace(C.Item,'_x0020_', ' ')
,Value = sum(C.Value)
From @YourTable A
Cross Apply (Select XMLData= cast((Select A.* for XML RAW) as xml)) B
Cross Apply (
Select Item = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','int')
From B.XMLData.nodes('/row') as C1(n)
Cross Apply C1.n.nodes('./@*') as C2(a)
Where a.value('local-name(.)','varchar(100)') not in ('Fields','ToExclude')
) C
Group By C.Item
退货
Item Value
col 1 6
col 2 6
col 1000 1
【讨论】:
【参考方案6】:试试这个动态列生成Sql脚本
DECLARE @Sql nvarchar(max)
SET @Sql=( SELECT DISTINCT 'SELECT'+
STUFF((SELECT ', '+ ' SUM( '+ COLUMN_NAME +' ) AS '+ QUOTENAME( COLUMN_NAME )
FROM INFORMATION_SCHEMA.COLUMNS Where TABLE_NAME ='Tab1000'
FOR XML PATH (''),type).value('.','varchar(max)'),1,2,'')
+' From Tab1000'From INFORMATION_SCHEMA.COLUMNS Where TABLE_NAME ='Tab1000')
EXEC (@sql)
【讨论】:
我收到此错误:“操作数数据类型 varchar 对 sum 运算符无效。”有什么想法吗? 你的表中列的数据类型是什么 更新的问题有一个更具体的例子。 “test_score”列的内容都是 INT 类型。我想按姓名分组,然后按人的姓氏对 1000 个“test_score”列中的每一个进行求和。如果我硬编码 1000 列,我可以做到这一点。我只是不知道如何做动态sql等价物。 @Srini131 - 您需要排除name
列,例如Where TABLE_NAME ='Tab1000' AND COLUMN_NAME <> 'Name'
以上是关于如何动态计算 GROUP 中多列的总和?的主要内容,如果未能解决你的问题,请参考以下文章