【问题标题】:Many SQL rows into one多条 SQL 行合二为一
【发布时间】:2015-11-18 13:41:24
【问题描述】:

我有一个存储过程,它连接多个表以生成一个大型结果集,然后将其返回到我的应用程序。应用程序依次循环遍历结果并组合特定 ID 上的行,并选择每行的数据以包含在新对象中。这可能是最容易用一个例子来解释的:

Inspection, Desc,         Value
1,          Description1, 3       
1,          Description2, 2
1,          Description3, 5

这是在代码中变成的

Inspection, Description1, Description2, Description3
1,          3,            2,            5

重点是每个检验项目有一行,项目描述作为标题,值作为检验行和标题的单元格值。然后将其导出到 Excel。

问题是:我如何在 SQL Server 中执行此操作,例如扩展我的 SP 以返回更少但“更宽”的行和更多列?

另一个复杂情况是,一项检查可能包含另一项缺少的行,在这种情况下,解决方案是添加一个空值或“-”。

附:这是使用 Sql Server 2012。

【问题讨论】:

  • 使用pivot,它会完成这项工作

标签: c# sql sql-server excel sql-server-2012


【解决方案1】:

如果您使用的是 mssql 2005+。您可以像这样使用枢轴:

测试数据

DECLARE @tbl TABLE(Inspection INT, [Desc] VARCHAR(100),Value INT)
INSERT INTO @tbl
VALUES
(1,'Description1', 3),     
(1,'Description2', 2),
(1,'Description3', 5)

查询

SELECT
    *
FROM
(
SELECT
    tbl.Inspection,
    tbl.[Desc],
    tbl.Value
FROM
    @tbl AS tbl
) AS tbl
PIVOT
(
    SUM(Value)
    FOR [Desc] IN ([Description1],[Description2],[Description3])
)AS pvt

结果:

Inspection, Description1, Description2, Description3
1           3             2             5

编辑

正如朱哈尔在评论中所说:

生成的列名(表中的值)是构建查询时的结果。这可能需要另一个初始查询来获取

编辑 2

如果您使用的不是 mssql 2005+。或者想要有和替代的解释。请参阅以下查询:

SELECT
    tbl.Inspection,
    SUM(CASE WHEN [Desc]='Description1' THEN tbl.Value ELSE 0 END) AS Description1,
    SUM(CASE WHEN [Desc]='Description2' THEN tbl.Value ELSE 0 END) AS Description2,
    SUM(CASE WHEN [Desc]='Description3' THEN tbl.Value ELSE 0 END) AS Description3
FROM
    @tbl AS tbl
GROUP BY
    tbl.Inspection

这不需要枢轴,可以在大多数 RDMS 上使用

【讨论】:

  • 应该注意这要求 OP 在构建查询时知道结果列名(表中的值)是什么。这可能需要另一个初始查询才能获取。
  • @juharr :更新了答案
【解决方案2】:

您应该使用 Sql Server Pivot。它将行转换为列。您可以通过this example 轻松开始。

【讨论】:

  • 答案可能很草率。您应该提供完整的答案,而不仅仅是“删除”功能/关键字/机制并完成它。
  • @Amit 我肯定会这样做,如果有任何代码或我可以解释的东西。对于这种得到建议类型的问题,这是我能回答的最佳答案。
  • 我认为您可以提供一个最小的示例来说明提供问题中的示例数据如何工作,或者我会评论问题本身,解释为什么这些问题不适合 SO(因为它们会导致像您这样的答案),甚至可能投票赞成。但这只是我的意见...... :-)(附注:请参阅@Arion 的回答)
  • 这对我来说没关系,如果它解决了我试图问的问题,我会尽快尝试并接受您的回答:) 如果没有,我将不得不重新提出问题。跨度>
  • 同意该示例数据。将来会这样做:)
【解决方案3】:

如果您想动态执行此操作,而不必知道所有 Desc 值是什么,您可以构建您的数据透视查询并使用 Exec()Execute sp_executesql

DECLARE @Columns NVARCHAR(MAX),
        @Sql NVARCHAR(MAX)

--Build your column headers based on Distinct Desc values
SELECT  @Columns = COALESCE(@Columns + ',', '') + QUOTENAME([Desc])
FROM    (SELECT DISTINCT [Desc] FROM tbl) t
ORDER BY [Desc]

--Build your pivot query
SET @Sql = '
    SELECT 
        *
    FROM
        tbl
    PIVOT
    (
        MAX([Value])
        FOR [Desc] IN (' + @Columns + ')
    ) p
'
EXEC(@Sql)

如果您希望 - 用于空值,则需要创建另一个变量来保存您的 sql 中 Select 部分的转换脚本。

DECLARE @Columns NVARCHAR(MAX),
        @Sql NVARCHAR(MAX),
        @ColumnAliases NVARCHAR(MAX)

--Build your pivot columns based on Distinct Desc values
SELECT  @Columns = COALESCE(@Columns + ',', '') + QUOTENAME([Desc])
FROM    (SELECT DISTINCT [Desc] FROM tbl) t
ORDER BY [Desc]

--Build your column headers, replacing NULL with -
SELECT  @ColumnAliases = COALESCE(@ColumnAliases + ',', '') 
            + 'COALESCE(CONVERT(VARCHAR,' + QUOTENAME([Desc]) + '),''-'') AS ' + QUOTENAME([Desc])
FROM    (SELECT DISTINCT [Desc] FROM tbl) t
ORDER BY [Desc]


--Build your pivot query
SET @Sql = '
    SELECT 
        Inspection,'
        + @ColumnAliases + '
    FROM
        tbl
    PIVOT
    (
        MAX([Value])
        FOR [Desc] IN (' + @Columns + ')
    ) p
'
EXEC(@Sql)

【讨论】:

  • 谢谢。这个提供了最多的提示来解决我的问题,将其标记为已接受的答案。
猜你喜欢
  • 1970-01-01
  • 2016-12-29
  • 2012-11-08
  • 2021-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多