志銘-2021年10月8日 00:57:00
0.背景說明
在T-SQL——數據透視和逆透視文中介紹了透視和逆透視
使用PIVOT
對結果集進行數據透視
SELECT FROM Table PIVOT(任意聚合函數(聚合字段) FOR 待擴展字段名 IN (待擴展元素集)) AS T
其中的 待擴展元素集需要一個靜態列表
簡單的說:IN(待擴展元素集)
,是不支持動態的子查詢
即不能這樣寫IN (select 帶擴展元素 from table)
所以帶擴展元素集需要我們就直接羅列出來即可
SELECT * FROM PIVOT(SUN(Scores) FOR Subject IN (語文,數學,外語))
若是實際開發中,需要動態的 查詢待擴展元素集
這就需要我們使用動態SQL取構建查詢語句,並執行查詢
1.准備測試數據
- 學生成績表
----學生成績表
IF OBJECT_ID('tempdb..#tempStudent') IS NOT NULL
BEGIN
DROP TABLE #tempStudent
END
CREATE TABLE #tempStudent
(
[Name] varchar(4),
[SubjectName] varchar(4),
[Scores] int,
[Class] varchar(10) )
INSERT INTO #tempStudent
VALUES
( '張三', '語文', 100, '八年級一班' ),
( '張三', '數學', 90, '三年級二班' ),
( '張三', '英語', 80, '三年級二班' ),
( '李四', '語文', 90, '三年級二班' ),
( '李四', '數學', 70, '三年級二班' ),
( '李四', '英語', 60, '三年級二班' )
- 考試科目表
----學生科目表
IF OBJECT_ID('tempdb..#tempSubject') IS NOT NULL
BEGIN
DROP TABLE #tempSubject;
END;
CREATE TABLE #tempSubject
(
[SubjectName] VARCHAR(4)
);
INSERT INTO #tempSubject
VALUES
('語文'),
('數學'),
('英語');
2.示例1——利用SELECT循環賦值
這里假定我們有單獨的待擴展元素集的表,比如說這里我們有獨立的課程名稱表#tempSubject
所以我們可以單獨的查詢后拼接出靜態的待擴展元素集
----拼接待擴展元素集
DECLARE @subjectStr VARCHAR(100)='';--注意一定要初始化為空字符串,才能實現下面的累加
SELECT @subjectStr=@subjectStr+SubjectName+',' FROM #tempSubject--拼接擴展元素集
SET @subjectStr =LEFT(@subjectStr,len(@subjectStr)-1)--刪除拼接的最后一個逗號
SELECT @subjectStr--返回:語文,數學,英語
----將透視SQL語句定義為字符串,並執行
DECLARE @sql NVARCHAR(1000) = 'SELECT * FROM #tempStudent PIVOT(SUM(Scores) FOR [SubjectName] IN ({@subjectStr}))T';--使用“{@subjectStr}”做占位符
SET @sql = REPLACE(@sql, '{@subjectStr}', @subjectStr);--替換占位符
SELECT @sql;
EXEC sp_executesql @sql;
-- 結果
-- Name Class 語文 數學 英語
-- ---- ---------- ----------- ----------- -----------
-- 張三 八年級一班 100 NULL NULL
-- 李四 三年級二班 90 70 60
-- 張三 三年級二班 NULL 90 80
假設我們沒有單獨維護待擴展元素集的數據表,即這里沒有提供#tempSubjcet
我看可以直接去重查詢后#tempStudent中的SubjectName字段值,進行拼接
----拼接待擴展元素集
DECLARE @subjectStr VARCHAR(100) = '';
WITH cteSubject
AS
(
SELECT DISTINCT SubjectName FROM #tempStudent
)
SELECT @subjectStr = @subjectStr + SubjectName + ',' FROM cteSubject; --拼接擴展元素集
SET @subjectStr = LEFT(@subjectStr, LEN(@subjectStr) - 1); --刪除拼接的最后一個逗號
SELECT @subjectStr; --返回:語文,數學,英語
--……后續操作如示例1
3.示例2——使用游標
- 若是有必要的話,可以使用游標拼接待擴展元素集
DECLARE @sql NVARCHAR(1000),
@subjectStr VARCHAR(1000),
@first INT;
--創建游標
DECLARE curStudent CURSOR FAST_FORWARD FOR
SELECT DISTINCT SubjectName FROM #tempStudent;
SET @first = 1;--標志變量:用於區分是否是第一個拼接字符串
SET @sql = N'SELECT * FROM #tempStudent PIVOT(SUM(Scores) FOR [SubjectName] IN (';
OPEN curStudent;
FETCH NEXT FROM curStudent INTO @subjectStr;
WHILE @@fetch_status = 0
BEGIN
IF @first = 0
SET @sql = @sql + N',';
ELSE
SET @first = 0;
SET @sql = @sql + @subjectStr;
FETCH NEXT FROM curStudent INTO @subjectStr;
END;
CLOSE curStudent;
DEALLOCATE curStudent;
SET @sql = @sql + N')) AS T;';
EXEC sp_executesql @sql;
4.示例3——使用FOR XML PATH()
其實針對構造"value1,value2,value3"格式的字符串,使用FOR XML PATH()
函數配合STUFF()
函數,是極其的方便
DECLARE @subjectStr VARCHAR(100);
WITH cteStudent AS
(
SELECT DISTINCT SubjectName FROM #tempStudent
)
SELECT @subjectStr= STUFF((SELECT ',' + SubjectName FROM cteStudent FOR XML PATH('')),1,1,'')
--這里@subjectStr=數學、英語、語文
DECLARE @sql NVARCHAR(1000) = 'SELECT * FROM #tempStudent PIVOT(SUM(Scores) FOR [SubjectName] IN ({@subjectStr}))T';--使用“{@subjectStr}”做占位符
SET @sql = REPLACE(@sql, '{@subjectStr}', @subjectStr);--替換占位符
SELECT @sql;
EXEC sp_executesql @sql;