SQL Server连接查询返回冗余行,如何实现动态车库列透视?
动态实现行列转换(适配Garage新增)
首先,你的需求属于典型的**行列转换(Pivot)**场景,而且因为Garage的值未来会新增,静态写死列的方法扩展性太差,推荐用动态SQL自动生成列。下面分不同数据库给你具体实现方案:
1. 静态实现(适合Garage暂时固定的场景)
如果只是想快速得到目标结果,静态CASE WHEN写法最直接,不用复杂逻辑:
SELECT m.ID, m.Desc, SUM(CASE WHEN t.Garage = 1 THEN t.Qty ELSE NULL END) AS Garage1Qty, SUM(CASE WHEN t.Garage = 2 THEN t.Qty ELSE NULL END) AS Garage2Qty, SUM(CASE WHEN t.Garage = 3 THEN t.Qty ELSE NULL END) AS Garage3Qty, SUM(CASE WHEN t.Garage = 4 THEN t.Qty ELSE NULL END) AS Garage4Qty FROM tblMaster m LEFT JOIN tblTrans t ON m.ID = t.IDMaster GROUP BY m.ID, m.Desc ORDER BY m.ID;
但这个方法的问题很明显:新增Garage后必须手动修改SQL,加对应的CASE分支,长期来看维护成本很高。
2. 动态SQL实现(适配Garage新增,推荐)
这个方法会自动读取tblTrans里所有已存在的Garage值,动态生成对应的列,完全不用手动维护。
针对SQL Server的写法:
DECLARE @cols NVARCHAR(MAX), @query NVARCHAR(MAX); -- 自动生成所有Garage对应的CASE语句和列名 SET @cols = STUFF((SELECT DISTINCT ', SUM(CASE WHEN t.Garage = ' + CAST(Garage AS NVARCHAR) + ' THEN t.Qty ELSE NULL END) AS Garage' + CAST(Garage AS NVARCHAR) + 'Qty' FROM tblTrans FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''); -- 拼接完整的查询语句 SET @query = 'SELECT m.ID, m.Desc, ' + @cols + ' FROM tblMaster m LEFT JOIN tblTrans t ON m.ID = t.IDMaster GROUP BY m.ID, m.Desc ORDER BY m.ID;'; -- 执行动态生成的SQL EXEC sp_executesql @query;
针对MySQL的写法:
MySQL没有SQL Server的STUFF函数,用GROUP_CONCAT来生成列:
SET @cols = (SELECT GROUP_CONCAT(DISTINCT 'SUM(CASE WHEN t.Garage = ', Garage, ' THEN t.Qty ELSE NULL END) AS `Garage', Garage, 'Qty`' SEPARATOR ', ') FROM tblTrans); SET @query = CONCAT('SELECT m.ID, m.Desc, ', @cols, ' FROM tblMaster m LEFT JOIN tblTrans t ON m.ID = t.IDMaster GROUP BY m.ID, m.Desc ORDER BY m.ID;'); -- 预处理并执行SQL PREPARE stmt FROM @query; EXECUTE stmt; DEALLOCATE PREPARE stmt;
针对PostgreSQL的写法:
用string_agg生成列,结合EXECUTE执行动态SQL:
WITH garage_cols AS ( SELECT string_agg(DISTINCT 'SUM(CASE WHEN t.Garage = ' || Garage || ' THEN t.Qty ELSE NULL END) AS "Garage' || Garage || 'Qty"', ', ') AS cols FROM tblTrans ) SELECT format('SELECT m.ID, m.Desc, %s FROM tblMaster m LEFT JOIN tblTrans t ON m.ID = t.IDMaster GROUP BY m.ID, m.Desc ORDER BY m.ID;', cols) INTO @query FROM garage_cols; EXECUTE @query;
关键说明:
- 用
LEFT JOIN是为了保证即使某个Type(比如未来新增的Type3)没有任何交易记录,也会在结果里显示,对应的Qty列都是null; - 动态SQL会自动识别
tblTrans里新增的Garage值,下次执行时会自动生成新的列(比如Garage5Qty); - 如果你的Garage值不是数字(比如字符串),只需要调整CASE里的判断条件,比如SQL Server里用
QUOTENAME(Garage, '''')来处理字符串类型的Garage值。
内容的提问来源于stack exchange,提问作者Lmao
相关产品推荐
相关产品推荐

