动态列下CHECK_CARD表Unpivot报错:SELECT关键字语法错误求助
动态SQL实现CHECK_CARD表转置的问题解决
需求回顾
你需要转置CHECK_CARD表,原表结构如下:
| anc_report_date | RiskSignal | group_company_attr |
|---|---|---|
| 2019-01-01 00:00:00.0000000 | NoRiskSignal | 4894543 |
| 2016-07-01 00:00:00.0000000 | RiskSignal | 1242151 |
期望转换为:
| anc_report_date | 2019-01-01 00:00:00.0000000 | 2016-07-01 00:00:00.0000000 |
|---|---|---|
| RiskSignal | NoRiskSignal | RiskSignal |
| group_company_attr | 4894543 | 1242151 |
问题分析
你之前的代码报错核心原因是直接把SELECT查询语句拼到了UNPIVOT的IN子句里——UNPIVOT要求IN子句里是明确的列名/值列表,不能嵌套查询。从你PRINT出的SQL就能看到,IN里直接塞了一整段SELECT逻辑,这必然触发语法错误。
正确解决方案
要实现动态转置,我们需要分两步处理:
- 先把需要作为列的所有日期值,拼接成带引号、逗号分隔的字符串列表
- 结合
UNPIVOT和PIVOT完成行转列操作(先把原表的列转成行,再把日期行转成列)
以下是完整的可执行代码:
-- 第一步:获取所有需要作为列的日期,拼接成带QUOTENAME的字符串列表 DECLARE @Column NVARCHAR(MAX); -- SQL Server 2017及以上版本用STRING_AGG(更简洁) SELECT @Column = STRING_AGG(QUOTENAME(anc_report_date), ', ') FROM ( SELECT DISTINCT cc.anc_report_date FROM CHECK_CARD cc INNER JOIN CHECK_CARD chc ON cc.group_company_attr = chc.group_company_attr AND cc.anc_report_date <= chc.anc_report_date AND chc.id = 1832307 WHERE cc.status = 1 ) AS Dates; -- 如果是SQL Server 2016及以下版本,用FOR XML PATH做兼容拼接 /* SELECT @Column = STUFF(( SELECT ', ' + QUOTENAME(anc_report_date) FROM ( SELECT DISTINCT cc.anc_report_date FROM CHECK_CARD cc INNER JOIN CHECK_CARD chc ON cc.group_company_attr = chc.group_company_attr AND cc.anc_report_date <= chc.anc_report_date AND chc.id = 1832307 WHERE cc.status = 1 ) AS Dates FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 2, ''); */ -- 第二步:构建动态转置SQL并执行 DECLARE @Transpose NVARCHAR(MAX); SET @Transpose = N' SELECT * FROM ( -- UNPIVOT:把原表的RiskSignal、group_company_attr转成属性名+属性值的行 SELECT anc_report_date, attribute_name, attribute_value FROM CHECK_CARD UNPIVOT ( attribute_value FOR attribute_name IN (RiskSignal, group_company_attr) ) AS unpvt ) AS src -- PIVOT:把不同的anc_report_date转成列,用MAX取唯一值(因为每个组合只有一个值) PIVOT ( MAX(attribute_value) FOR anc_report_date IN (' + @Column + ') ) AS pvt; '; EXEC sp_executesql @Transpose;
代码解释
- 拼接日期列表:用
STRING_AGG(或兼容旧版本的FOR XML PATH)把查询到的日期转换成[2019-01-01 00:00:00.0000000], [2016-07-01 00:00:00.0000000]格式,QUOTENAME用于包裹日期,避免特殊字符导致语法错误。 - UNPIVOT阶段:把原表中
RiskSignal和group_company_attr两列转成两行数据,每行包含日期、属性名(比如"RiskSignal")和属性值(比如"NoRiskSignal")。 - PIVOT阶段:把不同的日期值转成列,用
MAX聚合函数是因为每个属性名+日期的组合只有唯一值,MAX不会改变结果,只是满足PIVOT对聚合函数的强制要求。
内容的提问来源于stack exchange,提问作者Nareg
相关产品推荐
相关产品推荐

