当前位置: 首页 > 科技观察

如果您知道如何在SQL中使用这个古老的功能,请举手!

时间:2023-03-15 14:56:52 科技观察

最近的每日题中,有一道题是将同类型的数据合并成一行。如果使用SQLServer2017及以上版本,可以直接使用STRING_AGG()函数,但2016以下版本没有此功能。是的,怎么解决?今天给大家介绍一下FORXMLPATH。它用于处理低版本数据库中的数据合并。是比较老的功能了,新版本还是可以用的。什么是FORXMLPATHFORXMLPATH就是将查询结果集以XML形式显示出来,在同一行中显示多行结果。我们通过实例向您介绍它的神奇之处。创建测试数据我们创建一个表CREATETABLEStu_Hobby(Stu_NameNVARCHAR(20),--nameAgeINT,--ageHobbyNVARCHAR(20)--hobby)INSERTINTOStu_HobbyVALUES(N'张三',19,N'踢足球'),(N'张三',19,N'打篮球'),(N'张三',19,N'游泳'),(N'李四',21,N'看电影'),(N'李四'Si',21,N'读书'),(N'王舞',22,N'唱歌'),(N'王舞',22,N'打游戏'),(N'麻六',19,N'踢足球'),(N'赵琪',20,N'攀登'),(N'赵琪',20,N'跑步')查询学生爱好表中的数据Stu_Hobby:如何使用Introduction测试数据建立后,我们开始查询这张表中的数据,使用FORXMLPATH。选择*FROMdbo.Stu_HobbyFORXMLPATH;结果如下:会生成一段XML代码,我们点击这行代码,会弹出一整个XML页面,由于篇幅较长,我们只截取一部分,如下:另外,我们也可以在FORXMLPATH后面写上参数。如果后面跟着参数,节点会被替换成参数名,例如:SELECT*FROMdbo.Stu_HobbyFORXMLPATH(hobby)结果如下图:变成了我们添加的参数。比较贴近我们实际需求的是下面这个功能。我们可以单独输出某个字段的值。比如我们要查看学生爱好表中Hobby列的具体值,可以这样写:SELECTHobby+','FROMdbo.Stu_HobbyFORXMLPATH('')注意:上面的+是字段拼接,即,用+将两个字符串连接成一个字符串。然后我们删除XML。结果如下:可以看到把我们写的所有兴趣爱好都列出来了,而且没有去重,可以理解为显示该列的所有值。具体实际应用现在我们要将上面学生表中每个学生的兴趣爱好单独显示一行,用“,”隔开。SELECTA.Stu_Name,A.Age,(SELECTHobby+','FROM[dbo].Stu_HobbyWHERE--必须加上匹配条件Stu_Name=A.Stu_NameANDAge=A.AgeFORXMLPATH(''))ASHobbyFROM[dbo].Stu_HobbyAGROUPBYA.Stu_Name,A.Age见证奇迹的时刻到了!!!与我们之前建的表相比,Hobby列的数据已经改为每个学生一行。上面的WHERE条件是必须的,去掉会怎么样?让我们注释掉WHERE条件,看看会发生什么?SELECTA.Stu_Name,A.Age,(SELECTHobby+','FROM[dbo].Stu_Hobby--WHERE--必须添加的匹配条件--Stu_Name=A.Stu_NameANDAge=A.AgeFORXMLPATH(''))ASHobbyFROM[dbo].Stu_HobbyAGROUPBYA.Stu_Name,A.Age结果如下:Hobby栏的所有值都会显示出来,很明显这不是我们想要的结果代码优化。不知道小伙伴们有没有注意到,Hobby栏的结果最后多了一个“,”。看起来很尴尬。有什么办法可以去除吗?答案是肯定的。首先使用LEFT()和LEN()函数来处理Hobby列SELECTT.Stu_Name,T.Age,LEFT(T??.Hobby,LEN(T.Hobby)-1)ASHobbyFROM(SELECTA.Stu_Name,A.Age,(SELECTHobby+','FROM[dbo].Stu_HobbyWHERE--必须加上的匹配条件Stu_Name=A.Stu_NameANDAge=A.AgeFORXMLPATH(''))ASHobbyFROM[dbo].Stu_HobbyAGROUPBYA.Stu_Name,A.Age)T结果为如下:这样我们的需求就完美解决了,但是这段代码有点长,能不能短点?答案是肯定的!在简化代码之前,我们需要先介绍一个要一起使用的函数:STUFF()STUFF()函数的作用STUFF()函数用于删除指定长度的字符,并且可以在该处插入另一组字符指定的起点。在STUFF()函数中,如果起始位置或长度值为负数,或者起始位置大于第一个字符串的长度,则返回空字符串。如果要删除的长度大于第一个字符串的长度,则最多删除第一个字符串中的第一个字符。STUFF()函数语法STUFF(character_expression,start,length,character_expression)参数说明character_expression:字符数据表达式。character_expression可以是常量、变量或一列字符或二进制数据。start:一个整数值,指定删除和插入的起始位置。如果start或length为负数,则返回空字符串。如果start比第一个character_expression长,则返回空字符串。start可以是bigint类型。length:一个整数,指定要删除的字符数。如果length比第一个character_expression长,则删除直到最后一个character_expression中的最后一个字符。length可以是bigint类型。返回类型如果character_expression是受支持的字符数据类型,则返回字符数据。如果character_expression是受支持的二进制数据类型,则返回二进制数据。注意1.如果起始位置或长度值为负数,或者起始位置大于第一个字符串的长度,将返回空字符串。如果要删除的长度大于第一个字符串的长度,则最多删除第一个字符串中的第一个字符。2.如果结果值大于返回类型支持的最大值,则产生错误。--以上信息来自微软官方文档。定义晕了。让我们来看看如何使用它。示例:SELECTSTUFF('abcdefg',1,0,'1234')--结果为'1234abcdefg'SELECTSTUFF('abcdefg',1,1,'1234')--结果为'1234bcdefg'SELECTSTUFF('abcdefg',2,1,'1234')--结果是'a1234cdefg'SELECTSTUFF('abcdefg',2,2,'1234')--结果是'a1234defg'说了这么多,来看看STUFF是怎么解决我们的以上问题,以上代码:SELECTA.Stu_Name,A.Age,STUFF((SELECT','+HobbyFROM[dbo].Stu_HobbyWHERE--必须添加的匹配条件Stu_Name=A.Stu_NameANDAge=A.AgeFORXMLPATH('')),1,1,'')ASHobbyFROM[dbo].Stu_HobbyAGROUPBYA.Stu_Name,A.Age比LEFT短吗?让我们看看结果是否是我们想要的。完美的!好了,FORXMLPATH的介绍到此结束。小伙伴们可以对比一下上面两种优化方式,比较一下哪种方式更容易理解。