用于SQL Server的CONCAT_WS()

时间:2013-10-17 16:33:39

标签: sql sql-server tsql

如何在SQL Server中模拟MySQL的CONCAT_WS()函数?

此函数类似于CONCAT() function in SQL Server 2012,只是它在非NULL项之间添加了一个分隔符:

SELECT id, CONCAT_WS('; ', a, b, c, d) AS bar
FROM foo
ORDER BY id;
| ID | BAR        |
|----|------------|
|  1 | a; b; c; d |
|  2 | b; d       |
|  3 | a; d       |
|  4 |            |

MySQL Fiddle

6 个答案:

答案 0 :(得分:13)

我们可以使用几个技巧:

  • 要跳过NULL值:COALESCE()
  • 要避免尾随分隔符:在每个项目之前添加它,之后删除第一个分隔符STUFF()

他是working example

CREATE TABLE foo (
  id INT IDENTITY(1, 1) NOT NULL,
  a VARCHAR(50),
  b VARCHAR(50),
  c VARCHAR(50),
  d VARCHAR(50),
  PRIMARY KEY (id)
);

INSERT INTO foo (a, b, c, d) VALUES ('a', 'b', 'c', 'd');
INSERT INTO foo (a, b, c, d) VALUES (NULL, 'b', NULL, 'd');
INSERT INTO foo (a, b, c, d) VALUES ('a', NULL, NULL, 'd');
INSERT INTO foo (a, b, c, d) VALUES (NULL, NULL, NULL, NULL);
SELECT id,
STUFF(
    COALESCE('; ' + a, '') +
    COALESCE('; ' + b, '') +
    COALESCE('; ' + c, '') +
    COALESCE('; ' + d, ''),
1, 2, '') AS bar
FROM foo
ORDER BY id
| ID | BAR        |
|----|------------|
|  1 | a; b; c; d |
|  2 | b; d       |
|  3 | a; d       |
|  4 | (null)     |

STUFF(..., 1, 2, '')的目的是删除初始分隔符(2是我们案例中的分隔符长度。)

这应该适用于SQL Server 2005(可能还有早期版本)。

注意:与原始CONCAT_WS()不同,当所有项目均为NULL时,我们的版本会返回NULL。老实说,我认为这是一个更好的选择,但无论如何都应该很容易改变。

答案 1 :(得分:6)

另一种方法是use a FOR XML subquery这样:

SELECT
  id,
  bar = STUFF(
    (
      SELECT '; ' + v
      FROM (VALUES (a), (b), (c), (d)) AS v (v)
      FOR XML PATH (''), TYPE
    ).value('.[1]', 'varchar(max)'),
    1, 2, ''
  )
FROM foo
ORDER BY id;

一方面,这看起来肯定比一系列COALESCE电话更复杂。另一方面,这更接近原型,因为分隔符只指定一次。

使用的语法至少需要SQL Server 2008+,但是如果VALUES构造函数更改为

SELECT a UNION ALL
SELECT b UNION ALL
SELECT c UNION ALL
SELECT d

该查询也将在SQL Server 2005中运行。

答案 2 :(得分:3)

SQL Server 2017 开始,您可以使用内置CONCAT_WS

  

<强> CONCAT_WS

     

使用第一个参数中指定的分隔符连接可变数量的参数。 (CONCAT_WS表示与分隔符连接。)

CONCAT_WS ( separator, argument1, argument1 [, argumentN]… ) 
     

处理NULL值

     

CONCAT_WS忽略SET CONCAT_NULL_YIELDS_NULL {ON | OFF}设置。

     

如果所有参数都为null,则为varchar(1)类型的空字符串   返回。

     

在连接期间忽略空值,并且不添加   分隔器。这有助于连接的常见场景   通常具有空值的字符串,例如第二个地址字段。   见例B.

     

如果您的方案需要在分隔符中包含空值,   使用ISNULL函数查看示例C.

所以你可以使用你的初始查询:

SELECT id, CONCAT_WS('; ', a, b, c, d) AS bar
FROM foo
ORDER BY id;

<强> db<>fiddle demo

答案 3 :(得分:3)

对于SQL Server 2012,您可以使用单个CONCAT替换过多的COALESCE来简化已接受的答案:

WITH tests(a, b, c, d) AS (
    SELECT NULL, NULL, NULL, NULL UNION
    SELECT NULL, NULL, NULL,  'd' UNION
    SELECT NULL, NULL,  'c', NULL UNION
    SELECT NULL, NULL,  'c',  'd' UNION
    SELECT NULL,  'b', NULL, NULL UNION
    SELECT NULL,  'b', NULL,  'd' UNION
    SELECT NULL,  'b',  'c', NULL UNION
    SELECT NULL,  'b',  'c',  'd' UNION
    SELECT  'a', NULL, NULL, NULL UNION
    SELECT  'a', NULL, NULL,  'd' UNION
    SELECT  'a', NULL,  'c', NULL UNION
    SELECT  'a', NULL,  'c',  'd' UNION
    SELECT  'a',  'b', NULL, NULL UNION
    SELECT  'a',  'b', NULL,  'd' UNION
    SELECT  'a',  'b',  'c', NULL UNION
    SELECT  'a',  'b',  'c',  'd'
)
SELECT a, b, c, d,
STUFF(CONCAT(
    '; ' + a,
    '; ' + b,
    '; ' + c,
    '; ' + d
), 1, 2, '') AS cat
FROM tests
a    | b    | c    | d    | cat
-----+------+------+------+-----------
NULL | NULL | NULL | NULL | NULL
NULL | NULL | NULL | d    | d
NULL | NULL | c    | NULL | c
NULL | NULL | c    | d    | c; d
NULL | b    | NULL | NULL | b
NULL | b    | NULL | d    | b; d
NULL | b    | c    | NULL | b; c
NULL | b    | c    | d    | b; c; d
a    | NULL | NULL | NULL | a
a    | NULL | NULL | d    | a; d
a    | NULL | c    | NULL | a; c
a    | NULL | c    | d    | a; c; d
a    | b    | NULL | NULL | a; b
a    | b    | NULL | d    | a; b; d
a    | b    | c    | NULL | a; b; c
a    | b    | c    | d    | a; b; c; d

答案 4 :(得分:0)

我用FOR XML PATH做。
您可以使用union(UNION ALL)而不是VALUES;这有一个额外的价值,它仍然适用于SQL-Server 2005(我们仍然需要在我们公司支持它),你可以删除NULL值。

DECLARE @in_SearchTerm1 nvarchar(100) 
DECLARE @in_SearchTerm2 nvarchar(100) 
DECLARE @in_SearchTerm3 nvarchar(100) 
DECLARE @in_SearchTerm4 nvarchar(100) 

SET @in_SearchTerm1 = N'a'
SET @in_SearchTerm2 = N''
SET @in_SearchTerm3 = N'c'
SET @in_SearchTerm4 = N''

SELECT 
    COALESCE
    (
        STUFF
        (
            (
                SELECT ' / ' + RPT_SearchTerm AS [text()]
                FROM 
                (
                                  SELECT NULLIF(@in_SearchTerm1, N'') AS RPT_SearchTerm, 1 AS RPT_Sort 
                        UNION ALL SELECT NULLIF(@in_SearchTerm2, N'') AS RPT_SearchTerm, 2 AS RPT_Sort  
                        UNION ALL SELECT NULLIF(@in_SearchTerm3, N'') AS RPT_SearchTerm, 3 AS RPT_Sort 
                        UNION ALL SELECT NULLIF(@in_SearchTerm4, N'') AS RPT_SearchTerm, 4 AS RPT_Sort 
                ) AS tempT 
                WHERE RPT_SearchTerm IS NOT NULL 
                ORDER BY RPT_Sort 
                FOR XML PATH(N''), TYPE 
            ).value('.', 'nvarchar(MAX)') 
            ,1
            ,3
            ,N''
        )
        ,N''
    ) AS RPT_SearchTerms 

注意使用nvarchar - 已经使用varchar停止。
您还必须订购它,以便保留顺序。

那是做什么的:

目标:
在报告中的4个单独过滤器中输入4个搜索项。
' / '连接的报告中显示这4个搜索字词。
如果搜索字词为空,则不应为' / / '
它应该按顺序显示,即term1 / term2 / term3 / term4,而不是例如term4 / TERM2 / TERM3 / TERM1。

如何:
因此,您将4个搜索项加入到联合中,并添加排序值以保留顺序。

您可以从联合中选择搜索项和分隔符(separatur + null = null)

SELECT ' / ' + RPT_SearchTerm 
FROM (UNION OF SEARCH TEMRS) AS tempT

按RPT_Sort订购

现在将tempT中的所有值(分隔符+文本)选择为一个XML文件(FOR XML),其中所有值都是带有空标记名(PATH(N'')的XML元素),然后选择值XML文本(AS [text()])(又名element.innerXML)。

将结果作为XML元素(TYPE)获取并检索该XML元素(.value('.', 'nvarchar(MAX)'))的内部文本字符串(也称为XML-decode)。

最后,删除前导'/'(STUFF(var, 1,3, N'')

原则上与

完全相同
CONCAT_WS(' / ', @in_SearchTerm1, @in_SearchTerm2, @in_SearchTerm3, @in_SearchTerm4)

现在添加nullif,

CONCAT_WS(' / ', NULLIF(@in_SearchTerm1, '') , NULLIF(@in_SearchTerm2, ''), NULLIF(@in_SearchTerm3, ''), NULLIF(@in_SearchTerm4, ''))

你在那儿。

这就是你仍然可以在SQL-Server中进行CONCAT_WS ...

答案 5 :(得分:0)

我知道这是老帖子,但我遇到同样的问题。

  

我只是使用CONCAT()函数。

我在各个字段中保存了地址行,我想加入所有行来创建地址。

我发现CONCAT()函数可以处理NULL并用空字符串替换它。 如果有什么加上NULL也是NULL。

所以我使用常规的CONCAT()函数并在每个地址行的末尾添加空格,所以如果该行为NULL则组合输出为空

SELECT 
    CONCAT(Address01 + ' ', Address02 + ' ', Address03 + ' ', Address04) AS Address 
FROM myTable