【问题标题】:How to pivot without knowing fixed columns in T-SQL如何在不知道 T-SQL 中的固定列的情况下进行透视
【发布时间】:2020-06-05 10:25:17
【问题描述】:

我有一个名为 balance 的表,我希望对其进行转置,但这非常困难,因为列名将标记为 1、2、3,并且余额将按每个客户的降序排序。

这是当前表格的截图:

还有数据透视表:

我查看了其他视频并发布了,但我没有找到与我目前的情况相匹配的解决方案。所以最终结果将是客户将按 asc 排序,余额将按 desc 排序。因此,对于客户 3,最高余额 500 将放在第 1 列中,300 在第 2 列中,250 在第 3 列中。

创建示例数据的脚本:

select Customer, Balance
into #a
from (
values
  (1,     250), 
  (2,     500), 
  (1,     205), 
  (2,     600), 
  (2,     700),
  (3,     300),
  (3,     500),
  (3,     250)
) v (Customer, Balance)

【问题讨论】:

  • 另外请不要使用图片,使用格式化文本。
  • @daleK SQL SSMS
  • 仅供参考:SSMS 不是 RDBMS,它是 SQL Server 的客户端接口。
  • 简短回答:你不能。 T-SQL 不支持运行时定义或参数化的标识符(如列名)。您必须使用 Dynamic-SQL(请不要)或在应用程序代码中执行 PIVOT。如果您需要在 SQL Server 中处理透视数据,请仅使用带有 PIVOT/UNPIVOT 的动态 SQL。
  • 好的@Dai。我知道在这种情况下我必须使用动态 SQL。我的问题是如何将值按降序传递到列表 [1]、[2]、[3] 的列?如果例如客户 2,只有 2 个条目,那么 0 将被放置在第 3 列中。

标签: sql-server tsql pivot-table ssms ssms-2017


【解决方案1】:

Demo on db<>fiddle

您可以使用ROW_NUMBER()标记值的个数,例如:1, 2, 3

注意ORDER BY [Balance] DESC 来获取你想要的生成值。

DECLARE 
    @columns NVARCHAR(MAX) = '',
    @sql     NVARCHAR(MAX) = '';


 SELECT Customer, Balance, Col = ROW_NUMBER() OVER (PARTITION BY Customer ORDER BY [Balance] DESC)
 into #b
 FROM #a

SELECT @columns += QUOTENAME(Col) + ','
from (SELECT DISTINCT Col FROM #b) A

-- remove the last comma
SET @columns = LEFT(@columns, LEN(@columns) - 1);


SET @sql = 'SELECT * FROM ( SELECT Customer, Balance, Col FROM  #b) src PIVOT( MAX([Balance]) FOR Col IN ('+ @columns +')) AS pivot_table;';

-- execute the dynamic SQL
EXECUTE sp_executesql @sql;

输出

更新

因为连接字符串is undocumented and unreliable。它并不总是按预期工作。所以你应该用下面的2个解决方案来解决

  1. 使用STRING_AGG(从 SQL Server 2017 及后期开始)
SELECT STRING_AGG(QUOTENAME(Col), ', ')
from (SELECT DISTINCT Col FROM #b) A
// Output: [1], [2], [3]
  1. 使用XML Extensions
DECLARE  @columns NVARCHAR(MAX) = ''
SELECT @columns = (
  SELECT QUOTENAME(Col) + ', '
  FROM (SELECT DISTINCT Col FROM #b) A

  FOR XML PATH(''), TYPE
                   ).value('.','varchar(max)')
SELECT @columns 
// Output: [1], [2], [3],

感谢@GarethD 的评论。查看db<>fiddle

【讨论】:

  • 使用变量赋值连接字符串 (SELECT @Column += ....) 是undocumented and unreliable. It does not always work as expected。您应该使用STRING_AGG(如果使用支持它的版本),或者使用XML Extensions 连接行。
  • 感谢您的评论。我会尽快更新您的建议。因为我在手机上在线。
  • 加雷斯D。哇。你很博学。我将问题归咎于这一行(SELECT @Column += ....)。但我就是不知道如何调整它以实现我真正想要的。你想得很好。 phong 当我在执行时使用我的源数据时,列将是 [3]、[1]、[2]。我真的希望它是 [1], [2], [3] 一旦程序被执行。
  • 我刚刚更新了我的答案,请看一下。感谢您的建议@GarethD
  • @JasonX 我刚刚更新了我的答案。请查看并给我您的反馈。
【解决方案2】:

试试这个。

declare @balanceAMTCols varchar(max)='',
@statement nvarchar(4000)=''

 select @balanceAMTCols= stuff( 
            (
                select ','+ QUOTENAME(balanceamtcol) from (
             select  distinct   convert(varchar(10),(row_number() over(partition by Customer order by Customer))) 
            balanceamtcol FROM #a   ) as tbl
            for xml path('')),1,1,'')

set @statement= 'select Customer,'+@balanceAMTCols+' from (
    select  Customer,Balance,convert(varchar(10),(row_number() over(partition by Customer order by Customer))) as balanceamtcol  FROM #a  
) [CustomerTbl]
pivot
(
    max(Balance)
    for balanceamtcol
    in ('+@balanceAMTCols+') ) as pivatetble'

exec sp_executesql  @statement

【讨论】:

    【解决方案3】:

    你可以试试这个方法

    select *,concat('Price',RANK() OVER (PARTITION BY i.REFERENCE_NO ORDER BY i.TASK_ID 
     DESC)) AS Rank  into #temp from [dbo].[Table_1] i
    
     select REFERENCE_NO,Price1,Price2,Price3 from 
       (
        select REFERENCE_NO,TASK_ID,Rank from #temp
        ) as cte 
        PIVOT (SUM(TASK_ID)
        FOR rank IN (Price1,Price2,Price3 ))
        as PVT
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-19
      相关资源
      最近更新 更多