求助:将含多聚合表头的Access透视查询转换为MSSQL
Access透视查询转SQL Server实现方案
没问题!我帮你把Access的TRANSFORM透视查询转换成SQL Server的版本,专门解决你提到的「表头包含多个字段组合」的透视难题——核心思路是先把需要作为表头的多个字段拼接成单个列,再用透视语法实现。
先理清楚原查询逻辑
你的原Access查询是基于多张表关联,筛选2017年发货、排除特定分组、特定质量等级和数据集的记录,最终按ID_SaleDest作为行,聚合Tons的总和,同时用多个字段组合作为列表头(你原代码的PIVOT部分没写完,我假设是QualPack和Groups的字段组合,你可以根据实际调整)。
方案1:静态透视(已知所有表头值)
如果提前知道所有要作为表头的字段组合值,可以直接写静态透视:
SELECT ID_SaleDest, -- 这里替换成实际的表头组合值,比如[Q1-GroupA]、[Q2-GroupB] [Q1-GroupA] AS [Q1-GroupA_总吨数], [Q2-GroupB] AS [Q2-GroupB_总吨数] FROM ( -- 子查询:准备透视所需的基础数据,拼接表头字段 SELECT Sales.ID_SaleDest, -- 把需要作为表头的多个字段拼接成一个列,这里用QualPack和Groups的字段举例 CONCAT(QualPack.QualityCode, '-', Groups.GroupName) AS PivotColumn, Sales.Tons FROM QualPack INNER JOIN SaleDest ON QualPack.ID_QualPack = SaleDest.ID_QualPack INNER JOIN Groups ON Groups.ID_Group = SaleDest.ID_Group INNER JOIN Customers ON Customers.ID_Customer = SaleDest.ID_Customer INNER JOIN Sales ON SaleDest.ID_SaleDest = Sales.ID_SaleDest WHERE YEAR(Sales.Date_shipped) = 2017 AND SaleDest.ID_Group <> 10 AND QualPack.Quality_Second <> 'NO' AND Sales.ID_Dataset IN ('AC', 'BU') -- 补充你原代码中未完成的WHERE条件 ) AS SourceData PIVOT ( -- 对应Access的TRANSFORM SUM(Sales.Tons) SUM(Tons) -- 指定用拼接后的列作为透视表头 FOR PivotColumn IN ([Q1-GroupA], [Q2-GroupB]) ) AS PivotTable;
方案2:动态透视(表头值不确定)
如果表头的字段组合值是动态变化的,就需要用动态SQL自动生成列列表:
DECLARE @PivotColumns NVARCHAR(MAX); DECLARE @SQL NVARCHAR(MAX); -- 第一步:自动生成所有需要透视的列名(拼接后的字段组合) SELECT @PivotColumns = STRING_AGG(QUOTENAME(CONCAT(QualPack.QualityCode, '-', Groups.GroupName)), ', ') FROM QualPack INNER JOIN SaleDest ON QualPack.ID_QualPack = SaleDest.ID_QualPack INNER JOIN Groups ON Groups.ID_Group = SaleDest.ID_Group INNER JOIN Customers ON Customers.ID_Customer = SaleDest.ID_Customer INNER JOIN Sales ON SaleDest.ID_SaleDest = Sales.ID_SaleDest WHERE YEAR(Sales.Date_shipped) = 2017 AND SaleDest.ID_Group <> 10 AND QualPack.Quality_Second <> 'NO' AND Sales.ID_Dataset IN ('AC', 'BU') GROUP BY QualPack.QualityCode, Groups.GroupName; -- 第二步:构建动态SQL语句 SET @SQL = N' SELECT ID_SaleDest, ' + @PivotColumns + ' FROM ( SELECT Sales.ID_SaleDest, CONCAT(QualPack.QualityCode, ''-'', Groups.GroupName) AS PivotColumn, Sales.Tons FROM QualPack INNER JOIN SaleDest ON QualPack.ID_QualPack = SaleDest.ID_QualPack INNER JOIN Groups ON Groups.ID_Group = SaleDest.ID_Group INNER JOIN Customers ON Customers.ID_Customer = SaleDest.ID_Customer INNER JOIN Sales ON SaleDest.ID_SaleDest = Sales.ID_SaleDest WHERE YEAR(Sales.Date_shipped) = 2017 AND SaleDest.ID_Group <> 10 AND QualPack.Quality_Second <> ''NO'' AND Sales.ID_Dataset IN (''AC'', ''BU'') -- 补充你原代码中未完成的WHERE条件 ) AS SourceData PIVOT ( SUM(Tons) FOR PivotColumn IN (' + @PivotColumns + ') ) AS PivotTable;'; -- 执行动态SQL EXEC sp_executesql @SQL;
关键注意事项
- 表头字段调整:如果原Access的
PIVOT是单个字段而非组合,直接把CONCAT(...)换成对应的单个字段即可(比如QualPack.QualityCode)。 - 旧版本SQL Server兼容:如果你的SQL Server版本低于2017,
STRING_AGG不支持,需要用FOR XML PATH来生成列列表,替换第一步的代码即可。 - 未完成的WHERE条件:你原代码里的
WHERE ((( Year([Date_shipped]))=2017) ... OR ((( Year([Date_...没写完,记得把剩余条件补充到两个方案的WHERE子句中。 - 单引号转义:动态SQL里的单引号需要用两个单引号
''表示,避免语法错误。
内容的提问来源于stack exchange,提问作者Ladybug




