本文转载请联系DBA杂念公众号。在SQLServer中,可能会有这样的拼接字符串的需求。需要将查询的列拼接成一个字符串。如下例所示,我们需要拼接AddressLine1字段,AddressID<=10,分隔符为|。如下图所示。这个方法好像没什么问题,简单测试一下就OK了:USEAdventureWorks2014;GODECLARE@address_listNVARCHAR(MAX);SET@address_list='';SELECT@address_list=@address_list+AddressLine1+'|'FROM[Person].[地址]WHEREAddressID<=10;SELECT@address_list但是,如果SQL中多了一个排序操作,结果就会发生变化。此SQL变量@address_list仅获取最后一条记录"9833Mt.DiasBlv.|",USEAdventureWorks2014;GODECLARE@address_listNVARCHAR(MAX);SET@address_list='';SELECT@address_list=@address_list+AddressLine1+'|'FROM[Person].[Address]WHEREAddressID<=10ORDERBY1;SELECT@address_list但是如果你用一些其他的字段来排序,那就又好了。在各种实际生产环境中,可能会按某个字段排序,字符串拼接不正常。但是按某些字段排序是正常的。对例程有点困惑。下面下面简单简单构造一个案例useadventureworks2014;goCreatetableTest(idintnotnull,namenvarchar(100)notnull,sortidIntnotnull,constraintpk_testprimarykey(id)),4UNIONALLSELECT5,'Jimmy',5;DECLARE@name_listNVARCHAR(100);SET@name_list='';SELECT@name_list=@name_list+t.NAME+'|'FROMdbo.TESTtORDERBYt.SortID;SELECT@name_list;以上脚本测试all通常情况下,下面的测试会显示连接字符串只获取到最后一行记录。DECLARE@name_listNVARCHAR(100)='';SET@name_list=''SELECT@name_list=@name_list+t.NAME+'|'FROMdbo.TESTtWHEREIDIN(1,2,3)ORDERBYt.SortID;SELECT@name_list;在生产环境中也有各种神奇的现象。按其中一个字段排序是正常的,但是当你按另一个字段排序时就会出现这种现象。如果你修改上面测试表中字段的大小,然后测试下面的脚本,你会发现这不会发生:USEAdventureWorks2014;GODROPTABLEdbo.TEST;GOCREATETABLETEST(IDINTNOTNULL,NAMENVARCHAR(32)NOTNULL,SortIDINTNOTNULL,CONSTRAINTPK_TESTPRIMARYKEY(ID));INSERTINTOdbo.TESTSELECT1,'Kerry',1UNIONALLSELECT2,'Jerry',2UNIONALLSELECT3,'Ken',3UNIONALLSELECT4,'Richard',4UNIONALLSELECT5,'Jimmy',5;乍一看是个“Bug”,其实算不上是BUG,官方文档http://support.microsoft.com/kb/287515有介绍过这个现象,但是现在这个链接失效了,对应的搜索找不到链接(微软官方文档挺坑爹的,还不如Oracle搞得好,经常一个链接失效,好的情况是链接换了,坏的情况是这个,找不到atall),以下信息是其他资料引用的KB287515的内容:原来这个迭代级别的Concatenation/迭代级联是一个不受支持的特性。Microsoft知识库文章287515指出,当您将任何运算符或表达式应用于聚合串联查询的ORDERBY子句时,您可能会遇到意外结果。我们不对串联查询的正确性做出任何保证(例如使用变量赋值和数据检索inval特定顺序)。SQLServer2008中的查询输出可能会根据计划选择、表中的数据等发生变化。即使语法允许您编写混合有序行检索和变量赋值的SELECT语句,您也不应依赖于此始终如一地工作。聚合级联查询的正确行为是未定义的简单地说,虽然在语法上支持这种字符串级联,但它不能保证结果的正确性,聚合级联查询的行为是未定义的。如果要安全可靠地拼接字符串,有以下几种方法:使用游标循环处理拼接的字符串。使用XML查询拼接字符串方法一:DECLARE@name_listVARCHAR(512);SELECT@name_list=(SELECTt.NAME+'|'FROMdbo.TESTtWHEREIDIN(1,2,3)ORDERBYt.SortIDFORXMLPATH(''),TYPE).value('.','varchar(max)')SELECT@name_list;方法二:SELECTName+'|'AS'data()'FROMdbo.TESTWHEREIDIN(1,2,3)FORXMLPATH('');方法3.使用STUFF函数方法4.使用COALESCE函数。请注意,使用COALESCE可能无效。如果@name_list定义为VARCHAR(512)或VARCHAR(MAX)就可以了。DECLARE@name_listVARCHAR(100);SELECT@name_list=COALESCE(@name_list+',','')+NameFROMdbo.TESTWHEREIDIN(1,2,3)ORDERBYSortIDSELECT@name_list5。使用CRL聚合来连接字符串。6.如果SQLServer2017使用STRING_AGG实现。SELECTSRING_AGG(Name,'|')ASDepartmentsFROMdbo.TESTWHEREIDIN(1,2,3)SELECTSortID,STRING_AGG(Name,'|')ASDepartmentsFROMdbo.TESTWHEREIDIN(1,2,3)GROUPBYSortIDORDERBYSortID;参考资料:https://stackoverflow.com/questions/5538187/why-sql-server-ignores-vaules-in-string-concatenation-when-order-by-clause-speci/5538210#5538210https://stackoverflow.com/questions/194852/how-to-concatenate-text-from-multiple-rows-into-a-single-text-string-in-sql-serv
