在SQL Server中可能有这样的拼接字符串需求,需要将查询出来的一列拼接成字符串,如下案例所示,我们需要将AddressID <=10的AddressLine1拼接起来,分隔符为|。如下截图所示。这种方式看起来似乎没有什么问题,而且简单测试也是OK:

USE AdventureWorks2014;
GO
DECLARE @address_list NVARCHAR(MAX);
SET @address_list ='';
 
SELECT @address_list = @address_list + AddressLine1 + '|' FROM [Person].[Address] WHERE AddressID <=10;
 
SELECT @address_list

但是,如果SQL多了一个排序操作,结果就变了,这个SQL的变量@address_list只获取到了最后一条记录”9833 Mt. Dias Blv.|“,

USE AdventureWorks2014;
GO
DECLARE @address_list NVARCHAR(MAX);
SET @address_list ='';
 
SELECT @address_list = @address_list + AddressLine1 + '|' FROM [Person].[Address] WHERE AddressID <=10 ORDER BY 1;
 
SELECT @address_list

但是你使用其它一些字段排序的话,它又是OK的。在各种实际生产环境中,可能按某个字段排序,字符串拼接就不正常了。但是按有些字段排序又是正常的。有点搞不清套路。下面简单构造一个案例

USE AdventureWorks2014;
GO
CREATE TABLE TEST
(
    ID        INT NOT NULL
   ,NAME    NVARCHAR(100) NOT NULL 
   ,SortID  INT NOT NULL
   ,CONSTRAINT PK_TEST PRIMARY KEY (ID)
);
 
INSERT INTO dbo.TEST
SELECT 1, 'Kerry'  , 1 UNION ALL 
SELECT 2, 'Jerry'  , 2 UNION ALL
SELECT 3, 'Ken'    , 3 UNION ALL
SELECT 4, 'Richard', 4 UNION ALL
SELECT 5, 'Jimmy'  , 5;
 
DECLARE @name_list NVARCHAR(100);
SET @name_list='';
 
SELECT @name_list = @name_list + t.NAME + '|'
FROM dbo.TEST t
ORDER BY t.SortID;
 
SELECT @name_list;
 

上面脚本测试都正常,下面测试就会出现连接字符串只获取了最后一行记录的情况。

DECLARE @name_list NVARCHAR(100)='';
 
SET @name_list=' '
SELECT @name_list = @name_list + t.NAME + '| '
FROM dbo.TEST t
WHERE ID IN (1,2,3)
ORDER BY t.SortID;
 
SELECT @name_list;

在生产环境还有各种魔幻的现象,按其中一个字段排序是正常,换另外一个字段排序就出现这种现象。如果你将上面测试表的字段的大小修改一下,然后测试下面脚本,发现又不会出现这种情况:

USE AdventureWorks2014;
GO
DROP TABLE dbo.TEST;
GO
CREATE TABLE TEST
(
    ID         INT NOT NULL
   ,NAME       NVARCHAR(32) NOT NULL 
   ,SortID     INT NOT NULL
   ,CONSTRAINT PK_TEST PRIMARY KEY (ID)
);
 
INSERT INTO dbo.TEST
SELECT 1, 'Kerry'  , 1 UNION ALL 
SELECT 2, 'Jerry'  , 2 UNION ALL
SELECT 3, 'Ken'    , 3 UNION ALL
SELECT 4, 'Richard', 4 UNION ALL
SELECT 5, 'Jimmy'  , 5;

初看像一个“Bug”,但是它确实不是一个Bug,官方文档http://support.microsoft.com/kb/287515有介绍这个现象,但是目前现在这个链接失效了,搜索也找不到对应的链接了(微软的官方文档这一点是相当坑爹,不如Oracle做得好,经常一个链接失效,好的情况是链接换了,糟糕的情况就是这种,根本找不到了),下面的资料是在其它资料里面引用KB 287515的内容:

事实证明,此迭代级联/迭代拼接(iterative concatenation)的功能是不受支持的功能。 Microsoft知识库文章287515指出

You may encounter unexpected results when you apply any operators or expressions to the ORDER BY clause of aggregate concatenation queries.

we do not make any guarantees on the correctness of concatenation queries (like using variable assignments with data retrieval in a specific order). The query output can change in SQL Server 2008 depending on the plan choice, data in the tables etc. You shouldn't rely on this working consistently even though the syntax allows you to write a SELECT statement that mixes ordered rows retrieval with variable assignment.

The correct behavior for an aggregate concatenation query is undefined

简单来说,这样拼接字符串,虽然在语法上支持,但是却不能保证这样的结果正确性,聚合串联查询的行为是不确定的。如果想安全可靠的拼接字符串的话,有下面一些方式:

1: 使用游标循环循环处理拼接字符串。

2: 使用XML查询拼接字符串

方式1:

DECLARE @name_list VARCHAR(512);
 
SELECT  @name_list=
(
SELECT  t.NAME + '|'
FROM dbo.TEST t
WHERE ID IN (1,2,3)
ORDER BY t.SortID
FOR XML PATH(''), TYPE
).value('.', 'varchar(max)')
 
SELECT @name_list;

方式2:

SELECT Name + '|' AS 'data()' 
FROM dbo.TEST 
WHERE ID IN (1,2,3)
FOR XML PATH('');

方式3: 借助STUFF函数

注意,使用COALESCE有可能也是不行的。如果定义@name_list为 VARCHAR(512)或VARCHAR(MAX)则是OK的。

DECLARE @name_list VARCHAR(100);
SELECT @name_list = COALESCE(@name_list + ', ', '') + Name 
FROM dbo.TEST
WHERE ID IN (1,2,3)
ORDER BY SortID
 
 
SELECT @name_list

3: 使用CRL聚合拼接字符串。

4: 如果SQL Server 2017使用STRING_AGG实现。

 
SELECT  STRING_AGG(Name, '|') AS Departments
FROM dbo.TEST
WHERE ID IN (1,2,3)
 
 
SELECT SortID, STRING_AGG(Name, '|') AS Departments
FROM dbo.TEST
WHERE ID IN (1,2,3)
GROUP BY SortID
ORDER BY SortID;

参考资料:

https://stackoverflow.com/questions/5538187/why-sql-server-ignores-vaules-in-string-concatenation-when-order-by-clause-speci/5538210#5538210

https://stackoverflow.com/questions/194852/how-to-concatenate-text-from-multiple-rows-into-a-single-text-string-in-sql-serv

SQL Server解惑——为什么ORDER BY改变了变量的字符串拼接结果的更多相关文章

  1. SQL Server解惑——为什么你的查询结果超出了查询时间范围

    原文:SQL Server解惑--为什么你的查询结果超出了查询时间范围 废话少说,直接上SQL代码(有兴趣的测试验证一下),下面这个查询语句为什么将2008-11-27的记录查询出来了呢?这个是同事遇 ...

  2. SQL SERVER学习笔记:临时表与表变量

    本文主要摘自徐海蔚的<Microsoft SQL SERVER企业级平台管理实践> 表变量可以作为存储过程的返回参数,而临时表不行.(存疑?表值参数只在SQL SERVER2008才开始支 ...

  3. SQL Server解惑——对象命名的唯一性小结

    关于SQL Server数据库中的对象命名的唯一性问题.例如表.索引.约束等数据库对象,有时候DBA在做数据库维护时,经常要创建对象或重命名对象,此时就会遇到一个问题,对象命名的唯一性问题.虽然是一个 ...

  4. SQL Server解惑——标识列的限制和跳号现象

      1:每个表只能创建一个标识列. 如下测试所示,如果表中有一个标识列,新增一个标识列就会遇到错误"Multiple identity columns specified for table ...

  5. SQL Server解惑——查询条件IN中能否使用变量

    在SQL Server的查询条件中,能否在IN里面使用变量呢? 如果可以的话,有没有需要注意的地方或一些限制呢?在回答这个问题前,我们先来看看这个例子: IF EXISTS (SELECT 1 FRO ...

  6. Sql server函数的学习1(系统变量、错误函数、转换函数)

    一.系统变量的介绍和使用 1.@@ERROR 变量 2.@@SERVICENAME 变量 3.@@TOTAL_ERRORS 变量 4.@@TOTAL_READ 变量 5.@@VERSION 变量 二. ...

  7. sql server存储过程中SELECT 与 SET 对变量赋值的区别

    SQL Server 中对已经定义的变量赋值的方式用两种,分别是 SET 和 SELECT. 对于这两种方式的区别,SQL Server 联机丛书中已经有详细的说明,但很多时候我们 并没有注意,其实这 ...

  8. 【转】sql server存储过程中SELECT 与 SET 对变量赋值的区别

    转自:http://www.cnblogs.com/micheng11/archive/2008/07/08/1237905.html SQL Server 中对已经定义的变量赋值的方式用两种,分别是 ...

  9. sql server存储过程中SELECT 与 SET 对变量赋值的区别 转自Theo

    SQL Server 中对已经定义的变量赋值的方式用两种,分别是 SET 和 SELECT. 对于这两种方式的区别,SQL Server 联机丛书中已经有详细的说明,但很多时候我们 并没有注意,其实这 ...

随机推荐

  1. Python中sort、sorted的cmp参数废弃之后使用cmp_to_key实现类似功能

    Python2.1以前的排序比较方法只提供一个cmp比较函数参数,没有__lt__等6个富比较方法, Python 2.1引入了富比较方法,Python3.4之后作废了cmp参数.相应地从Python ...

  2. 记阿里云 RDS MySQL 的一个大坑

    花了一个下午的时间,终于把一个阿里云 RDS MySQL 的一个大坑填上了,解决方法令人匪夷所思!绝对会让各位看官感到大吃一惊,阿里云 RDS MySQL 居然有这样 xx 的大坑! 问题 最近应业务 ...

  3. flex:align-items和align-content的区别

    属性值 align-items的属性值有:baseline.center.flex-end.flex-start.stretch.inherit.initial.unset align-content ...

  4. 【坑点集合】C++ STL 使用注意事项整理

    Intro 简单整理了一些关于 C++ STL 的注意点. 虽然大多数东西可以手写不过某些东西最好少造轮子,善用 STL 可以节约很多考场时间,简化实现. 当然是时空限制和功能足够的前提下. Tips ...

  5. 深入分析 Java、Kotlin、Go 的线程和协程

    前言 协程是什么 协程的好处 进程 进程是什么 进程组成 进程特征 线程 线程是什么 线程组成 任务调度 进程与线程的区别 线程的实现模型 一对一模型 多对一模型 多对多模型 线程的"并发& ...

  6. 世界上最快的排序算法——Timsort

    前言 经过60多年的发展,科学家和工程师们发明了很多排序算法,有基本的插入算法,也有相对高效的归并排序算法等,他们各有各的特点,比如归并排序性能稳定.堆排序空间消耗小等等.但是这些算法也有自己的局限性 ...

  7. JavaSE19-IO特殊流和Properties集合

    1.IO特殊操作流 1.1 标准输入流 System类中有两个静态的成员变量 public static final InputStream in:标准输入流.通常该流对应于键盘输入或由主机环境或用户 ...

  8. day111:MoFang:邀请好友流程&生成邀请好友二维码&第三方应用识别二维码&本地编译测试&记录邀请人信息

    目录 1.邀请业务逻辑流程图 2.邀请好友-前端 3.邀请好友-后端接口(生成二维码) 4.前端获取后端生成的二维码 5.前端长按页面,保存图片到相册 6.客户端通过第三方识别微信二维码,服务端提供对 ...

  9. [日常摸鱼]poj2420 A Star not a Tree?

    题意:给定$n$个点,找一个点使得这个点到所有点的距离之和最小,求出这个最小距离 传说中的模拟退火- #include<cstdio> #include<ctime> #inc ...

  10. 保姆级教程,带你认识大数据,从0到1搭建 Hadoop 集群

    大数据简介,概念部分 概念部分,建议之前没有任何大数据相关知识的朋友阅读 大数据概论 什么是大数据 大数据(Big Data)是指无法在一定时间范围内用常规软件工具进行捕捉.管理和处理的数据集合,是需 ...