简体   繁体   English

SQL Server 的 CONCAT_WS()

[英]CONCAT_WS() for SQL Server

How can I emulate MySQL's CONCAT_WS() function in SQL Server?如何在 SQL Server 中模拟 MySQL 的CONCAT_WS()函数?

This function is similar to CONCAT() function in SQL Server 2012 except that it adds a separator between non-NULL items:此函数类似于SQL Server 2012 中的CONCAT()函数,不同之处在于它在非 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 ) ( MySQL 小提琴)

We can use a couple of tricks:我们可以使用一些技巧:

  • To skip NULL values: COALESCE()跳过NULL值: COALESCE()
  • To avoid trailing separator: add it before every item, remove the first one afterwards with eg STUFF()为了避免尾随分隔符:在每个项目之前添加它,然后使用例如STUFF()删除第一个

He's a 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)     |

The purpose of STUFF(..., 1, 2, '') is to remove the initial separator ( 2 is the separator length in our case). STUFF(..., 1, 2, '')是删除初始分隔符(在我们的例子中2是分隔符长度)。

This should work on SQL Server 2005 (and possibly earlier versions).这应该适用于 SQL Server 2005(可能还有更早的版本)。

Note: unlike the original CONCAT_WS() , our version returns NULL when all items are NULL .注:不像原来CONCAT_WS()我们的版本返回NULL ,当所有项目都NULL I honestly think it's a better choice but it should be easy to change anyway.老实说,我认为这是一个更好的选择,但无论如何它应该很容易改变。

Another approach would be to use a FOR XML subquery like this:另一种方法是使用 FOR XML 子查询,如下所示:

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;

On the one hand, this looks certainly more complicated than a series of COALESCE calls.一方面,这看起来肯定比一系列 COALESCE 调用更复杂。 On the other hand, this is closer to the prototype in that the delimiter is only specified once.另一方面,这更接近原型,因为分隔符只指定一次。

The syntax used requires at least SQL Server 2008+, but if the VALUES constructor is changed to使用的语法至少需要 SQL Server 2008+,但如果将 VALUES 构造函数更改为

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

the query will also run in SQL Server 2005.该查询还将在 SQL Server 2005 中运行。

Starting from SQL Server 2017 you could use built-in CONCAT_WS :SQL Server 2017开始,您可以使用内置的CONCAT_WS

CONCAT_WS CONCAT_WS

Concatenates a variable number of arguments with a delimiter specified in the 1st argument.使用第一个参数中指定的分隔符连接可变数量的参数。 (CONCAT_WS indicates concatenate with separator.) (CONCAT_WS 表示用分隔符连接。)

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

Treatment of NULL values NULL 值的处理

CONCAT_WS ignores the SET CONCAT_NULL_YIELDS_NULL {ON|OFF} setting. CONCAT_WS 忽略 SET CONCAT_NULL_YIELDS_NULL {ON|OFF} 设置。

If all the arguments are null, an empty string of type varchar(1) is returned.如果所有参数都为空,则返回 varchar(1) 类型的空字符串。

Null values are ignored during concatenation, and does not add the separator.连接期间将忽略空值,并且不添加分隔符。 This facilitates the common scenario of concatenating strings which often have blank values, such as a second address field.这有助于连接通常具有空白值的字符串的常见场景,例如第二个地址字段。 See example B.参见示例 B。

If your scenario requires null values to be included with a separator, see example C using the ISNULL function.如果您的方案要求使用分隔符包含空值,请参阅使用 ISNULL 函数的示例 C。

So you could use your initial query:所以你可以使用你的初始查询:

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

db<>fiddle demo db<>小提琴演示

For SQL Server 2012 you can simplify the accepted answer by replacing plethora of COALESCE with single CONCAT :对于 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

I do it with FOR XML PATH.我用 FOR XML PATH 来做。
You can use a union (UNION ALL) instead of VALUES;您可以使用联合 (UNION ALL) 代替 VALUES; this has the added value that it still works on SQL-Server 2005 (we still have to support it in our company), and you can remove NULL 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 

Note the use of nvarchar - stop using varchar already.注意 nvarchar 的使用 - 已经停止使用 varchar。
You also have to order it, in order to preserve sequence.您还必须订购它,以保持顺序。


So what does this do:那么这有什么作用:

Goal:目标:
Take the 4 search terms inputed in 4 separate filters in a report.获取在报告中的 4 个单独过滤器中输入的 4 个搜索词。
Display these 4 search terms in the report concatenated by ' / ' .在报告中显示这 4 个搜索词,用' / '连接。
There should not be a ' / / ' if a search term is empty.如果搜索词为空,则不应有' / / '
It should be displayed in sequence, that is term1/term2/term3/term4, and not eg term4/term2/term3/term1.它应该按顺序显示,即term1/term2/term3/term4,而不是例如term4/term2/term3/term1。

How to:如何:
So you take the 4 search terms into a union, and add a sort value to preserve order .因此,您将 4 个搜索词合并为一个联合,并添加一个排序值以保留 order 。

You select the search terms and the separator from the union (separatur + null = null)您从联合中选择搜索词和分隔符 (separatur + null = null)

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

Order it by RPT_Sort按 RPT_Sort 排序

Now select all the values (separater + text) in tempT into one XML file ( FOR XML ), where all values are XML-elements with an empty-tagname ( PATH(N'') , and select the values XML-text ( AS [text()] ) (aka element.innerXML).现在选择 tempT 中的所有值(分隔符 + 文本)到一个 XML 文件( FOR XML )中,其中所有值都是带有空标记名( PATH(N'') XML 元素,然后选择值 XML 文本( AS [text()] )(又名 element.innerXML)。

get the result of that as XML-element ( TYPE ) and retrieve the innerText string of that XML-element ( .value('.', 'nvarchar(MAX)') ) (aka XML-decode).将其结果作为 XML 元素( TYPE )获取并检索该 XML 元素的 innerText 字符串( .value('.', 'nvarchar(MAX)') )(又名 XML-decode)。

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

This is in principle exactly the same as这在原则上与

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

Now add nullif,现在添加 nullif,

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

and you're there.你就在那里。

This is how you're still able to do CONCAT_WS in SQL-Server ...这就是您仍然能够在 SQL-Server 中执行 CONCAT_WS 的方式...

i know this is old post but i come the same issue.我知道这是旧帖子,但我遇到了同样的问题。

i simply use CONCAT() function for this.我只是为此使用 CONCAT() 函数。

i have address lines saved in individual fields and i wanted to join all lines to create address.我在各个字段中保存了地址行,我想加入所有行来创建地址。

i found that CONCAT() function can handle NULL and replaces it with empty string.我发现 CONCAT() 函数可以处理 NULL 并将其替换为空字符串。 also if anything plus NULL is NULL.如果任何东西加上 NULL 是 NULL。

so i used regular CONCAT() function and add space at the end of each address line so if that line is NULL then combine output is null所以我使用了常规的 CONCAT() 函数并在每个地址行的末尾添加了空格,因此如果该行为 NULL,则合并输出为 null

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM