You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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]),用STUFFFOR 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

火山引擎 最新活动