志銘-2021年10月23日 10:43:21
0. 將結果集轉化為XML格式
- 測試數據
IF OBJECT_ID('tempdb..#tempStu') IS NOT NULL
BEGIN
DROP TABLE #tempStu;
END;
CREATE TABLE #tempStu
(
[Name] VARCHAR(4),
[SubjectName] VARCHAR(4),
[Scores] INT
);
INSERT INTO #tempStu
(
Name,
SubjectName,
Scores
)
VALUES
('張三', '語文', 90),
('李四', '語文', 100),
('王五', '語文', 80);
- FOR XML AUTO
- 表名作為節點名稱
- 一行作為一個節點,字段列作為節點屬性
--AUTO則默認節點的名稱為表名稱
SELECT * FROM #tempStu WHERE Name = '李四' FOR XML AUTO;
--結果:(這里使用的是臨時表,前面的_x0023_是數據庫默認生成的)
--<_x0023_tempStu Name="李四" SubjectName="語文" Scores="100" />
- FOR XML RAW
- 通過RAW() 參數設置節點名稱,無參默認節點名稱為row
- 一行作為一個節點,字段列作為節點屬性
--無參默認的使用row作為節點名稱
SELECT * FROM #tempStu FOR XML RAW
--結果:
--<row Name="李四" SubjectName="語文" Scores="100" />
--使用參數設置節點名稱
SELECT * FROM #tempStu WHERE Name = '李四' FOR XML RAW('Stu');
--結果:
--<Stu Name="李四" SubjectName="語文" Scores="100" />
- ELEMENTS
- 一行作為一個父節點,每一個字段作為一個子節點
SELECT Name,
SubjectName,
Scores
FROM #tempStu
FOR XML AUTO, ELEMENTS;
-- --結果:
-- <_x0023_tempStu>
-- <Name>張三</Name>
-- <SubjectName>語文</SubjectName>
-- <Scores>90</Scores>
-- </_x0023_tempStu>
-- <_x0023_tempStu>
-- <Name>李四</Name>
-- <SubjectName>語文</SubjectName>
-- <Scores>100</Scores>
-- </_x0023_tempStu>
-- <_x0023_tempStu>
-- <Name>王五</Name>
-- <SubjectName>語文</SubjectName>
-- <Scores>80</Scores>
-- </_x0023_tempStu>
- FOR XML PATH
- 一行一個父節點,每一個字段作為一個子節點
- PATH() 參數為父節點名稱,無參則父節點名稱默認為row
- 可以認為是FOR XML RAW ,ELEMENT的快捷方法
SELECT Name,
SubjectName,
Scores
FROM #tempStu
FOR XML PATH
-- 結果:
-- <Student>
-- <Name>張三</Name>
-- <SubjectName>語文</SubjectName>
-- <Scores>90</Scores>
-- </Student>
-- <Student>
-- <Name>李四</Name>
-- <SubjectName>語文</SubjectName>
-- <Scores>100</Scores>
-- </Student>
-- <Student>
-- <Name>王五</Name>
-- <SubjectName>語文</SubjectName>
-- <Scores>80</Scores>
-- </Student>
- 創建根節點:ROOT
SELECT * FROM #tempStu FOR XML RAW('Stu'),ROOT('Students')
--結果:
-- <Students>
-- <Stu Name="張三" SubjectName="語文" Scores="90" />
-- <Stu Name="李四" SubjectName="語文" Scores="100" />
-- <Stu Name="王五" SubjectName="語文" Scores="80" />
-- </Students>
1. 列值拼接為字符串
即實現某一列的列值累加為字符串,這個需求的實現方法有多種,可以參考我之前的博文T-SQL——透視PIVOT動態獲取待擴展元素集
該文中就是動態的將列值拼接為使用逗號隔開的字符串,
這里通過使用FOR XML PATH('')和STUFF()函數實現列值的拼接
DECLARE @subjectStr VARCHAR(100);
WITH cteStudent AS
(
SELECT '張三' AS Name,'語文'AS SubjectName,90 AS Scores
UNION ALL
SELECT '張三' AS Name,'數學'AS SubjectName,90 AS Scores
UNION ALL
SELECT '李四' AS Name,'語文'AS SubjectName,100 AS Scores
UNION ALL
SELECT '李四' AS Name,'數學'AS SubjectName,100 AS Scores
UNION ALL
SELECT '王五' AS Name,'語文'AS SubjectName,80 AS Scores
)
,Temp AS
(
SELECT DISTINCT SubjectName FROM cteStudent
)
SELECT @subjectStr =STUFF((SELECT ','+SubjectName FROM Temp FOR XML PATH('')),1,1,'')
SELECT @subjectStr
--結果:
--數學,語文
2. 字符串轉換為列值
將一串使用特定符號分隔的字符,轉換為一個表值。
實現這個功能的方法很多,可以參考我之前的博文
這里我們通過XML類型和REPLACE()函數實現字符串的分裂
--需要
DECLARE @str VARCHAR(MAX)='1,2,3,4,';
DECLARE @xmlstr XML;
SET ARITHABORT ON;
SET @xmlstr=CONVERT(XML, '<root><v>'+REPLACE(@str, ',', '</v><v>')+'</v></root>');
--SELECT @xmlstr
DECLARE @tableVar TABLE (F1 VARCHAR(100))
INSERT INTO @tableVar
SELECT F1=N.v.value('.', 'varchar(100)') FROM @xmlstr.nodes('/root/v') N(v);
SELECT * FROM @tableVar;
--結果
F1
--
1
2
3
4
--因為待分裂的字符串結尾也有一個逗號,所以這里是空字符串
上述操作在實際開發是中可以簡單的封裝為一個表值函數,便於重復使用
CREATE FUNCTION dbo.funSplitStr
(
@str varchar(1000),--待分裂的字符串
@separator varchar(10)--字符串中分隔使用的符號,比如說','
)
RETURNS @tableVar TABLE
(
F1 VARCHAR(100)
)
AS
BEGIN
DECLARE @xmlstr XML;
--SET ARITHABORT ON;
SET @xmlstr = CONVERT(XML, '<root><v>' + REPLACE(@str, @separator, '</v><v>') + '</v></root>');
--SELECT @xmlstr;
INSERT INTO @tableVar
SELECT F1 = N.v.value('.', 'varchar(100)') FROM @xmlstr.nodes('/root/v') N(v);
RETURN;
END;
GO
--測試該函數
DECLARE @str VARCHAR(100)='1,2,3,4,'
DECLARE @separator VARCHAR(10)=','
SELECT * FROM dbo.funSplitStr(@str,@separator)
--結果
F1
-------
1
2
3
4
--空字符串
- 示例:分組求和,並將聚合字段值使用“,”保留在一起
---測試數據
DECLARE @test TABLE
(
[Name] VARCHAR(4),
[Subject] VARCHAR(4),
[Grade] INT
);
INSERT INTO @test
VALUES
('張三', '語文', 100),
('張三', '數學', 90),
('李四', '語文', 90),
('李四', '數學', 80),
('李四', '英語', 70);
SELECT * FROM @test
SELECT Name,
Subjects= STUFF( (SELECT ','+Subject FROM @test WHERE Name=T1.Name FOR XML PATH ('')),1,1,'') ,
SUM(Grade) AS TotalGrade
FROM @test AS T1
GROUP BY Name
--結果:
Name Subjects Grade
----- ---------------- -----
李四 語文,數學,英語 240
張三 語文,數學 190
3. 一些說明
現在開發中,除了配置文件使用XML,我已經很少有需要使用XML的地方了,
SQL Server2000中便存在XML類型,2005中擴展了一些針對XML的操作函數。
而在SQL Server2016中已經支持JSON了,添加了許多處理JSON的內置函數。
但是,通過SQL Server中的XML類型處理字符串是及其便利的,如上文中的將字符串串轉為列值等。
其用處還體現在一些需要數據庫動態生成SQL語句的地方,總而言之就是方便與構造一些字符串
這里在舉一個例子,無實際的意義,但是可以開闊一下思維。
示例:數據庫層面構造簡單的JSON字符串
DECLARE @test TABLE
(
[Name] VARCHAR(4),
[Subject] VARCHAR(4),
[Grade] INT
);
INSERT INTO @test
VALUES
('張三', '語文', 100),
('張三', '數學', 90),
('李四', '語文', 90),
('李四', '數學', 80),
('李四', '英語', 70);
SELECT '['+STUFF((SELECT ',{"name":"'+Name+'","subject":"'+[Subject]+'","grade":"' +CAST(Grade AS VARCHAR(4))+'"}' FROM @test FOR XML PATH ('')),1,1,'') +']'
--結果:
-- [
-- {
-- "name": "張三",
-- "subject": "語文",
-- "grade": "100"
-- },
-- {
-- "name": "張三",
-- "subject": "數學",
-- "grade": "90"
-- },
-- {
-- "name": "李四",
-- "subject": "語文",
-- "grade": "90"
-- },
-- {
-- "name": "李四",
-- "subject": "數學",
-- "grade": "80"
-- },
-- {
-- "name": "李四",
-- "subject": "英語",
-- "grade": "70"
-- }
-- ]