【问题标题】:Trying to Sum up Cross-Tab Data in SQL尝试总结 SQL 中的跨表数据
【发布时间】:2018-06-14 17:24:53
【问题描述】:

我有一张表,其中每个 ID 都有一个或多个位置,每个位置都有一个计数。地点可以在 ID 中重复。它存储在这样的行中:

ID  ColumnName  DataValue
1   place1  ABC
1   count1  5
2   place1  BEC
2   count1  12
2   place2  CDE
2   count2  6
2   place3  BEC
2   count3  9
3   place1  BBC
3   count1  5
3   place2  BBC
3   count2  4

最终,我想要一个表,其中每个可能的地名都是它自己的列,并且每个 ID 的每个地方的计数被汇总,如下所示:

ID  ABC BEC CDE BBC
1   5   0   0   0
2   0   21  6   0
3   0   0   0   9

我不知道解决这个问题的最佳方法。大约有 50 种不同的可能地名,因此在查询中专门列出它们并不理想。我知道我最终必须对数据进行透视,但我不知道我应该在汇总计数之前还是之后进行。而且无论是之前还是之后,我都无法弄清楚如何去总结。

任何想法/帮助将不胜感激。在这一点上,我很难找到从哪里开始。我看过一些有类似问题的帖子,但没有比这更令人费解的了。

编辑: 现在我正在使用它来旋转表格,但这给我留下了名为 place1、place2、...的列。count1、count2、... 而且我不知道如何适当地总结计数并使用地名创建新列。

DECLARE @cols NVARCHAR(MAX), @query NVARCHAR(MAX);
SET @cols = STUFF(
                 (
                     SELECT DISTINCT
                            ','+QUOTENAME(c.[ColumnName])
                     FROM #temp c FOR XML PATH(''), TYPE
                 ).value('.', 'nvarchar(max)'), 1, 1, '');
SET @query = 'SELECT [ID], '+@cols+'from (SELECT [ID],
           [DataValue] AS [amount],
           [ColumnName] AS [category]
    FROM #temp
    )x pivot (max(amount) for category in ('+@cols+')) p';
EXECUTE (@query);

【问题讨论】:

  • 抱歉,这是 Microsoft SSMS
  • 一个ID可以拥有的位置数量是否有最大限制?如果是这样,你只需要一个支点。如果没有,您将需要使用动态 SQL 来计算最大位置数,然后动态编写 SQL 以对其进行透视。
  • 编写一个查询,将源数据表转换为具有列的数据集:id、place 和 count。然后以结果为中心
  • 没有限制。你能更详细地了解如何做支点吗?我拥有的枢轴代码将为我提供标记为 place1、place2、place3、...、count1、count2、count3、...的列,我不知道如何汇总具有相同地名的计数。换句话说,我不知道如何将 place1 与 count1 等相关联,并将计数与每个 ID 的相似地点相加
  • 您应该将数据透视查询添加到问题中。

标签: sql sql-server sum pivot crosstab


【解决方案1】:

你的表结构很糟糕。您需要先对数据进行规范化,然后才能尝试对其进行透视。试试这个:

;WITH IDs AS
(
    SELECT DISTINCT
        id
        ,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
        ,Place = datavalue
    FROM #temp
    WHERE ISNUMERIC(datavalue) = 0
)
,Counts AS
(
    SELECT DISTINCT
        id
        ,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
        ,Cnt   = CAST(datavalue AS INT)
    FROM #temp
    WHERE ISNUMERIC(datavalue) = 1
)
SELECT
    piv.id
    ,ABC = ISNULL(piv.ABC, 0)
    ,BEC = ISNULL(piv.BEC, 0)
    ,CDE = ISNULL(piv.CDE, 0)
    ,BBC = ISNULL(piv.BBC, 0)
FROM (SELECT i.id, i.Place, c.Cnt FROM IDs i JOIN Counts c ON c.id = i.id AND c.ColId = i.ColId) src
PIVOT (   SUM(Cnt)
          FOR Place IN ([ABC], [BEC], [CDE], [BBC])
      ) piv;

使用动态 SQL 会产生以下结果:

SET @query = 
';WITH IDs AS
(
    SELECT DISTINCT
        id
        ,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
        ,Place = datavalue
    FROM #temp
    WHERE ISNUMERIC(datavalue) = 0
)
,Counts AS
(
    SELECT DISTINCT
        id
        ,ColId = RIGHT(ColumnName, LEN(ColumnName) - 5)
        ,Cnt   = CAST(datavalue AS INT)
    FROM #temp
    WHERE ISNUMERIC(datavalue) = 1
)
SELECT [ID], '+@cols+'
FROM 
(
    SELECT i.id, i.Place, c.Cnt 
    FROM IDs i 
    JOIN Counts c ON c.id = i.id AND c.ColId = i.ColId
) src
PIVOT 
(SUM(Cnt) FOR Place IN ('+@cols+')) piv;';
EXECUTE (@query);

【讨论】:

  • 谢谢,我是 SQL 新手,我只是采用在其他地方看到的代码片段。我同意我必须使用的表非常糟糕,而且它不完全是它存储在数据库中的方式,而是我的同事为其他目的提取数据的方式。但我回到原始数据,我想我可能找到了另一个 ID,它将每个地方'n' 与 count'n' 联系起来。
  • 是的,你真的需要 place'n' 和 count'n' 之间的链接。我的回答很老套。
【解决方案2】:

试试这个:

SELECT id,
COALESCE(ABC, 0) AS ABC,
COALESCE(BBC, 0) AS BBC,
COALESCE(BEC, 0) AS BEC,
COALESCE(CDE, 0) AS CDE
FROM
(SELECT id,
 MIN(CASE WHEN columnname LIKE 'place%' THEN datavalue END) AS col,
 CAST(MIN(CASE WHEN columnname LIKE 'count%' THEN datavalue END) AS INT) AS val
 FROM t
 GROUP BY id, RIGHT(columnname, 1)
) src
PIVOT
(SUM(val)
 FOR col in ([ABC], [BBC], [BEC], [CDE])) pvt

在这里测试:http://rextester.com/XUTJ68690

在 src 查询中,您需要重新格式化您的数据,以便您在每一行中都有一个唯一的 id 和位置。从那里开始,一个支点将起作用。

【讨论】:

    【解决方案3】:

    如果计数总是紧跟在位置之后,则以下查询将生成一个数据集以进行透视。 透视前的结果数据集具有以下列: id、地名、计数

    select placeTable.id, placeTable.datavalue, countTable.datavalue
    from
    (select *, row_number() over (order by id, %%physloc%%) as rownum
    from test
    where isnumeric(datavalue) = 1  
    ) as countTable
    
    join 
    
    (select *, row_number() over (order by id, %%physloc%%) as rownum
    from test
    where isnumeric(datavalue) <> 1   
    ) as placeTable
    
    on countTable.id = placeTable.id and 
       countTable.rownum = placeTable.rownum
    

    在 sqlfidde mssqlserver 上测试:http://sqlfiddle.com/#!6/701c91/18

    【讨论】:

      【解决方案4】:

      这是另一种使用动态样式的 PIVOT 运算符的方法

      declare @Col varchar(2000) = '', 
              @Query varchar(2000) = ''
      
      set @Col = stuff( 
                        (select ','+QUOTENAME(DataValue) 
                                    from table where isnumeric(DataValue) = 0 
                                    group by DataValue for xml path('')),1,1,'')
      
      set @Query = 'select id, '+@Col+' from
                  (
                   select id, DataValue, 
                          cast((case when isnumeric(DataValue) = 1 then DataValue else lead(DataValue) over (order by id) end) as int) Value 
                   from table 
                  ) as a
                  PIVOT
                  (
                    sum(Value) for DataValue in ('+@Col+')
                  )pvt'
      
      EXECUTE (@Query)
      

      注意:如果我找到字符串值并用数字数据值替换,我已经使用 lead() 函数访问下一行数据

      结果:

      id  ABC  BBC  BEC  CDE
      1   5    NULL NULL NULL
      2   NULL NULL 21   6
      3   NULL 9    NULL NULL
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-12
        • 1970-01-01
        • 2011-02-05
        • 2012-06-25
        • 1970-01-01
        相关资源
        最近更新 更多