SQL Server动态透视实现问询:基于@tbl_price表的数据处理
SQL Server 动态透视实现方案(针对@tbl_price表)
你需要对包含日期、股票ID和价格的@tbl_price表实现动态透视,对吧?因为透视的列(比如日期或者股票ID)是动态变化的,没法提前硬编码,所以得用动态SQL来实现。下面我给你详细的解决方案:
首先先把你的表结构和完整数据补全(你提供的最后一行有点截断,我先补全方便测试):
declare @tbl_price table ([date] date, stockid varchar(50), price float) insert into @tbl_price ([date], stockid, price) values ('2018-03-01', '10001', 24.8), ('2018-03-02', '10001', 26.2), ('2018-03-03', '10001', 27.8), ('2018-03-04', '10001', 28.8), ('2018-03-05', '10001', 21.8), ('2018-03-06', '10001', 23.8), ('2018-03-01', '20003', 30.89), ('2018-03-02', '20003', 33.00), ('2018-03-03', '20003', 34.8), ('2018-03-04', '20003', 34.2), ('2018-03-05', '20003', 33.8), ('2018-03-06', '20003', 35.1)
接下来分两种常见的透视场景来实现:
场景1:将日期作为列,股票ID作为行,展示对应日期的价格
这是最常见的需求——每个股票ID一行,每一列对应一个日期的价格。实现步骤如下:
步骤1:生成动态列列表
首先我们需要把所有不同的日期转换成带方括号的列名格式(比如[2018-03-01]),用STUFF和FOR XML PATH来拼接成字符串:
DECLARE @cols NVARCHAR(MAX); SELECT @cols = STUFF( (SELECT ',' + QUOTENAME(CONVERT(VARCHAR(10), [date], 23)) FROM @tbl_price GROUP BY [date] ORDER BY [date] FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '' );
这里QUOTENAME用来给日期加上方括号,避免特殊字符冲突;ORDER BY [date]保证列按日期顺序排列。
步骤2:构建并执行动态透视SQL
接下来用生成的列列表构建PIVOT语句,然后执行:
DECLARE @pivotSQL NVARCHAR(MAX); SET @pivotSQL = N' SELECT stockid, ' + @cols + ' FROM ( SELECT stockid, [date], price FROM @tbl_price ) AS src PIVOT ( MAX(price) -- 因为每个(stockid, date)组合唯一,用MAX/MIN/AVG都可以 FOR [date] IN (' + @cols + ') ) AS pvt ORDER BY stockid; '; -- 执行动态SQL,注意因为是表变量,需要用EXEC sp_executesql并传递变量 EXEC sp_executesql @pivotSQL, N'@tbl_price TABLE ([date] date, stockid varchar(50), price float)', @tbl_price = @tbl_price;
注意:因为我们用的是表变量,所以执行动态SQL时需要通过sp_executesql把表变量传递进去,否则动态SQL内部无法访问@tbl_price。如果是永久表的话就不用这么麻烦,直接写表名即可。
场景2:将股票ID作为列,日期作为行,展示对应股票的价格
如果你的需求是每行对应一个日期,每列对应一个股票ID,只需要调整列列表和PIVOT的字段即可:
步骤1:生成股票ID的动态列列表
DECLARE @stockCols NVARCHAR(MAX); SELECT @stockCols = STUFF( (SELECT ',' + QUOTENAME(stockid) FROM @tbl_price GROUP BY stockid ORDER BY stockid FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '' );
步骤2:构建并执行动态SQL
DECLARE @stockPivotSQL NVARCHAR(MAX); SET @stockPivotSQL = N' SELECT [date], ' + @stockCols + ' FROM ( SELECT [date], stockid, price FROM @tbl_price ) AS src PIVOT ( MAX(price) FOR stockid IN (' + @stockCols + ') ) AS pvt ORDER BY [date]; '; EXEC sp_executesql @stockPivotSQL, N'@tbl_price TABLE ([date] date, stockid varchar(50), price float)', @tbl_price = @tbl_price;
关键说明
- 因为透视的列是动态生成的,所以必须用动态SQL来实现,静态PIVOT无法处理未知数量的列。
- 对于PIVOT中的聚合函数(比如
MAX(price)),因为每个(分组字段, 透视字段)的组合是唯一的(每个股票每天只有一个价格),所以用MAX、MIN、AVG结果都是一样的,任选其一即可。 - 如果是永久表(不是表变量),动态SQL里直接写表名就行,不用传递变量,会更简单。
内容的提问来源于stack exchange,提问作者hieko




