SQL Server 中的字符串连接

发布于 2025-01-08 15:58:42 字数 251 浏览 3 评论 0原文

考虑这样一种情况,我们在 SQL Server 2005 的 SP 中有两个变量,如下所示,

@string1 = 'a,b,c,d'
@string2 = 'c,d,e,f,g'

是否有解决方案可以从中获取新字符串(如 (@string1 U @string2) )而不使用任何循环。即最终的字符串应该是这样的,

@string3 = 'a,b,c,d,e,f,g'

Consider a situation we have two variables in SQL Server 2005's SP as below,

@string1 = 'a,b,c,d'
@string2 = 'c,d,e,f,g'

Is there a solution to get a new string out of that like (@string1 U @string2) without using any loops. i.e the final string should be like,

@string3 = 'a,b,c,d,e,f,g'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

固执像三岁 2025-01-15 15:58:42

如果您需要一组而不是一次一行地执行此操作。给定以下 split 函数:

USE tempdb;
GO
CREATE FUNCTION dbo.SplitStrings(@List nvarchar(max))
RETURNS TABLE
AS
   RETURN ( SELECT Item FROM
       ( SELECT Item = x.i.value(N'./text()[1]', N'nvarchar(max)')
         FROM ( SELECT [XML] = CONVERT(xml, '<i>'
         + REPLACE(@List,',', '</i><i>') + '</i>').query('.')
           ) AS a CROSS APPLY [XML].nodes('i') AS x(i) ) AS y
       WHERE Item IS NOT NULL
   );
GO

然后使用下表和示例数据以及字符串变量,您可以通过这种方式获得所有结果:

DECLARE @foo TABLE(ID INT IDENTITY(1,1), col NVARCHAR(MAX));

INSERT @foo(col) SELECT N'c,d,e,f,g';
INSERT @foo(col) SELECT N'c,e,b';
INSERT @foo(col) SELECT N'd,e,f,x,a,e';

DECLARE @string NVARCHAR(MAX) = N'a,b,c,d';

;WITH x AS
(
    SELECT f.ID, c.Item FROM @foo AS f
    CROSS APPLY dbo.SplitStrings(f.col) AS c
), y AS
(
    SELECT ID, Item FROM x
    UNION
    SELECT x.ID, s.Item
        FROM dbo.SplitStrings(@string) AS s
        CROSS JOIN x
)
SELECT ID, Items = STUFF((SELECT ',' + Item 
    FROM y AS y2 WHERE y2.ID = y.ID 
    FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)'), 1, 1, N'')
FROM y
GROUP BY ID;

结果:

ID   Items
--   ----------
 1   a,b,c,d,e,f,g
 2   a,b,c,d,e
 3   a,b,c,d,e,f,x

在较新的版本 (SQL Server 2017+) 上,查询要简单得多,并且您不需要不需要创建您自己的自定义字符串分割函数:

;WITH x AS
(
    SELECT f.ID, c.value FROM @foo AS f
    CROSS APPLY STRING_SPLIT
    (
      CONCAT(f.col, N',', @string), N','
    ) AS c GROUP BY f.ID, c.value
)
SELECT ID, STRING_AGG(value, N',')
WITHIN GROUP (ORDER BY value)
FROM x GROUP BY ID;

现在,您真正应该做的是遵循前面的内容建议并首先将这些内容存储在相关表中。每当发生插入或更新时,您可以使用相同类型的拆分方法来单独存储字符串,而不是仅仅将 CSV 转储到单个列中,并且您的应用程序实际上不必更改将数据传递到的方式你的程序。但取出数据肯定会更容易!

编辑

为 SQL Server 2008 添加一个潜在的解决方案,该解决方案稍微复杂一些,但只需少一个循环即可完成工作(使用大量表扫描和替换)。我不认为这比上面的解决方案更好,而且它的可维护性肯定较差,但如果您发现自己能够升级到 2008 或更高版本(也适用于任何 2008+ 用户),那么它是一个可以测试的选项。遇到这个问题)。

SET NOCOUNT ON;

-- let's pretend this is our static table:

CREATE TABLE #x
(
    ID int IDENTITY(1,1),
    col nvarchar(max)
);

INSERT #x(col) VALUES(N'c,d,e,f,g'), (N'c,e,b'), (N'd,e,f,x,a,e');

-- and here is our parameter:

DECLARE @string nvarchar(max) = N'a,b,c,d';

代码:

DECLARE @sql nvarchar(max) = N'DECLARE @src TABLE(ID INT, col NVARCHAR(32));
    DECLARE @dest TABLE(ID int, col nvarchar(32));';

SELECT @sql += '
    INSERT @src VALUES(' + RTRIM(ID) + ','''
    + REPLACE(col, ',', '''),(' + RTRIM(ID) + ',''') + ''');'
FROM #x;

SELECT @sql += '
    INSERT @dest VALUES(' + RTRIM(ID) + ','''
    + REPLACE(@string, ',', '''),(' + RTRIM(ID) + ',''') + ''');'
FROM #x;

SELECT @sql += '
    WITH x AS (SELECT ID, col FROM @src UNION SELECT ID, col FROM @dest)
    SELECT DISTINCT ID, Items = STUFF((SELECT '','' + col
     FROM x AS x2 WHERE x2.ID = x.ID FOR XML PATH('''')), 1, 1, N'''')
     FROM x;'

EXEC sys.sp_executesql @sql;
GO
DROP TABLE #x;

这在 2005 年要做起来要困难得多(尽管并非不可能),因为您需要将 VALUES() 子句更改为 UNION ALL...

In case you need to do this as a set and not one row at a time. Given the following split function:

USE tempdb;
GO
CREATE FUNCTION dbo.SplitStrings(@List nvarchar(max))
RETURNS TABLE
AS
   RETURN ( SELECT Item FROM
       ( SELECT Item = x.i.value(N'./text()[1]', N'nvarchar(max)')
         FROM ( SELECT [XML] = CONVERT(xml, '<i>'
         + REPLACE(@List,',', '</i><i>') + '</i>').query('.')
           ) AS a CROSS APPLY [XML].nodes('i') AS x(i) ) AS y
       WHERE Item IS NOT NULL
   );
GO

Then with the following table and sample data, and string variable, you can get all of the results this way:

DECLARE @foo TABLE(ID INT IDENTITY(1,1), col NVARCHAR(MAX));

INSERT @foo(col) SELECT N'c,d,e,f,g';
INSERT @foo(col) SELECT N'c,e,b';
INSERT @foo(col) SELECT N'd,e,f,x,a,e';

DECLARE @string NVARCHAR(MAX) = N'a,b,c,d';

;WITH x AS
(
    SELECT f.ID, c.Item FROM @foo AS f
    CROSS APPLY dbo.SplitStrings(f.col) AS c
), y AS
(
    SELECT ID, Item FROM x
    UNION
    SELECT x.ID, s.Item
        FROM dbo.SplitStrings(@string) AS s
        CROSS JOIN x
)
SELECT ID, Items = STUFF((SELECT ',' + Item 
    FROM y AS y2 WHERE y2.ID = y.ID 
    FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)'), 1, 1, N'')
FROM y
GROUP BY ID;

Results:

ID   Items
--   ----------
 1   a,b,c,d,e,f,g
 2   a,b,c,d,e
 3   a,b,c,d,e,f,x

On newer versions (SQL Server 2017+), the query is much simpler, and you don't need to create your own custom string-splitting function:

;WITH x AS
(
    SELECT f.ID, c.value FROM @foo AS f
    CROSS APPLY STRING_SPLIT
    (
      CONCAT(f.col, N',', @string), N','
    ) AS c GROUP BY f.ID, c.value
)
SELECT ID, STRING_AGG(value, N',')
WITHIN GROUP (ORDER BY value)
FROM x GROUP BY ID;

Now that all said, what you really should do is follow the previous advice and store these things in a related table in the first place. You can use the same type of splitting methodology to store the strings separately whenever an insert or update happens, instead of just dumping the CSV into a single column, and your applications shouldn't really have to change the way they're passing data into your procedures. But it sure will be easier to get the data out!

EDIT

Adding a potential solution for SQL Server 2008 that is a bit more convoluted but gets things done with one less loop (using a massive table scan and replace instead). I don't think this is any better than the solution above, and it is certainly less maintainable, but it is an option to test out should you find you are able to upgrade to 2008 or better (and also for any 2008+ users who come across this question).

SET NOCOUNT ON;

-- let's pretend this is our static table:

CREATE TABLE #x
(
    ID int IDENTITY(1,1),
    col nvarchar(max)
);

INSERT #x(col) VALUES(N'c,d,e,f,g'), (N'c,e,b'), (N'd,e,f,x,a,e');

-- and here is our parameter:

DECLARE @string nvarchar(max) = N'a,b,c,d';

The code:

DECLARE @sql nvarchar(max) = N'DECLARE @src TABLE(ID INT, col NVARCHAR(32));
    DECLARE @dest TABLE(ID int, col nvarchar(32));';

SELECT @sql += '
    INSERT @src VALUES(' + RTRIM(ID) + ','''
    + REPLACE(col, ',', '''),(' + RTRIM(ID) + ',''') + ''');'
FROM #x;

SELECT @sql += '
    INSERT @dest VALUES(' + RTRIM(ID) + ','''
    + REPLACE(@string, ',', '''),(' + RTRIM(ID) + ',''') + ''');'
FROM #x;

SELECT @sql += '
    WITH x AS (SELECT ID, col FROM @src UNION SELECT ID, col FROM @dest)
    SELECT DISTINCT ID, Items = STUFF((SELECT '','' + col
     FROM x AS x2 WHERE x2.ID = x.ID FOR XML PATH('''')), 1, 1, N'''')
     FROM x;'

EXEC sys.sp_executesql @sql;
GO
DROP TABLE #x;

This is much trickier to do in 2005 (though not impossible) because you need to change the VALUES() clauses to UNION ALL...

终弃我 2025-01-15 15:58:42

有两种方法可以做到这一点:

  • 构建一个 CLR 函数来为您完成这项工作。将逻辑移回 .NET 代码,这是更容易进行字符串操作的平台。

  • 如果您必须使用 SQL Server,那么您将需要:

将两个字符串“分解”为两个表,此函数可能会有所帮助: http://blog.logiclabz.com/sql-server/split-function-in-sql-server-to-break-comma-separated-strings-into-table.aspx

获取两个表中的唯一字符串列表。 (简单查询)

将两个字符串表“内爆”到一个变量中(http://stackoverflow.com/questions/194852/concatenate-many-rows-into-a-single-text-string)

Two ways you can do that:

  • Build a CLR function to do the job for you. Move the logic back to .NET code which is much easier platform for string manipulation.

  • If you have to use SQL Server, then you will need to:

"explode" the two strings into two tables, this function might help: http://blog.logiclabz.com/sql-server/split-function-in-sql-server-to-break-comma-separated-strings-into-table.aspx

Get a unique list of strings from the two tables. (simple query)

"implode" the two string tables into a variable (http://stackoverflow.com/questions/194852/concatenate-many-rows-into-a-single-text-string)

み青杉依旧 2025-01-15 15:58:42

找到此函数 dbo 相关答案中的.Split,您可以像这样使用this:

declare @string1 nvarchar(50) = 'a,b,c,d'
declare @string2 nvarchar(50) = 'c,d,e,f,g'

select * from dbo.split(@string1, ',')

select * from dbo.split(@string2, ',')

declare @data nvarchar(100) = ''

select @data =  @data +  ',' + Data from (
    select Data from dbo.split(@string1, ',')
    union
    select Data from dbo.split(@string2, ',')
) as d

select substring(@data, 2, LEN(@data))

最后一个 SELECT 返回

a,b,c,d,e,f,g

Found this function dbo.Split in a related answer, which you can use like this:

declare @string1 nvarchar(50) = 'a,b,c,d'
declare @string2 nvarchar(50) = 'c,d,e,f,g'

select * from dbo.split(@string1, ',')

select * from dbo.split(@string2, ',')

declare @data nvarchar(100) = ''

select @data =  @data +  ',' + Data from (
    select Data from dbo.split(@string1, ',')
    union
    select Data from dbo.split(@string2, ',')
) as d

select substring(@data, 2, LEN(@data))

The last SELECT returns

a,b,c,d,e,f,g
近箐 2025-01-15 15:58:42

抱歉

set @string3 = @string1+','+@string2

,不清楚您只想要独特的事件。您使用什么版本的 SQL Server?字符串操作函数因版本而异。

如果您不介意 UDF 分割字符串,请尝试以下操作:

CREATE FUNCTION dbo.Split
(
    @RowData nvarchar(2000),
    @SplitOn nvarchar(5)
)  
RETURNS @RtnValue table 
(
    Id int identity(1,1),
    Data nvarchar(100)
) 
AS  
BEGIN 
    Declare @Cnt int
    declare @data varchar(100)
    Set @Cnt = 1

    While (Charindex(@SplitOn,@RowData)>0)
    Begin
       Insert Into @RtnValue (data) 
     Select ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1)))
             Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
         Set @Cnt = @Cnt + 1
    End

    Insert Into @RtnValue (data)
    Select Data = ltrim(rtrim(@RowData))


    Return
END

以及使用 UDF 的代码

go
@string1 = 'a,b,c,d'
@string2 = 'c,d,e,f,g'

declare @string3 varchar(200)
set @string3 = ''
select @string3 = @string3+data+',' 
from ( select data,min(id) as Id from dbo.split(@string1+','+@string2,',') 
     group by data ) xx
order by xx.id

print left(@string3,len(@string3)-1)

How about

set @string3 = @string1+','+@string2

Sorry, wasn't clear you wanted only unique occurrences. What version of SQL server are you using? String manipulation functions vary per version.

If you don't mind a UDF to split the string, try this:

CREATE FUNCTION dbo.Split
(
    @RowData nvarchar(2000),
    @SplitOn nvarchar(5)
)  
RETURNS @RtnValue table 
(
    Id int identity(1,1),
    Data nvarchar(100)
) 
AS  
BEGIN 
    Declare @Cnt int
    declare @data varchar(100)
    Set @Cnt = 1

    While (Charindex(@SplitOn,@RowData)>0)
    Begin
       Insert Into @RtnValue (data) 
     Select ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1)))
             Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
         Set @Cnt = @Cnt + 1
    End

    Insert Into @RtnValue (data)
    Select Data = ltrim(rtrim(@RowData))


    Return
END

and the code to use the UDF

go
@string1 = 'a,b,c,d'
@string2 = 'c,d,e,f,g'

declare @string3 varchar(200)
set @string3 = ''
select @string3 = @string3+data+',' 
from ( select data,min(id) as Id from dbo.split(@string1+','+@string2,',') 
     group by data ) xx
order by xx.id

print left(@string3,len(@string3)-1)
听闻余生 2025-01-15 15:58:42

下面的 SQL 函数会将逗号分隔的列表转换为表变量...

CREATE FUNCTION [dbo].[udfCsvToTable]( @CsvString VARCHAR( 8000))
-- Converts a comman separated value into a table variable

RETURNS @tbl TABLE( [Value] VARCHAR( 100) COLLATE DATABASE_DEFAULT NOT NULL)

AS BEGIN

DECLARE @Text VARCHAR( 100)

SET @CsvString = RTRIM( LTRIM( @CsvString))
SET @CsvString = REPLACE( @CsvString, CHAR( 9), '')
SET @CsvString = REPLACE( @CsvString, CHAR( 10), '')
SET @CsvString = REPLACE( @CsvString, CHAR( 13), '')

IF LEN( @CsvString) < 1 RETURN

WHILE LEN( @CsvString) > 0 BEGIN

    IF CHARINDEX( ',', @CsvString) > 0 BEGIN

        SET @Text = LEFT( @CsvString, CHARINDEX( ',', @CsvString) - 1)
        SET @CsvString = LTRIM( RTRIM( RIGHT( @CsvString, LEN( @CsvString) - CHARINDEX( ',', @CsvString))))
    END
    ELSE BEGIN

        SET @Text = @CsvString
        SET @CsvString = ''
    END

    INSERT @tbl VALUES( LTRIM( RTRIM( @Text)))
END

RETURN
END

然后您可以将两个表合并在一起,就像这样...

SELECT * FROM udfCsvToTable('a,b,c,d')
UNION   
SELECT * FROM udfCsvToTable('c,d,e,f,g')

这将为您提供一个结果集



c
d
e
f

The following SQL function will convert a comma separated list to a table variable...

CREATE FUNCTION [dbo].[udfCsvToTable]( @CsvString VARCHAR( 8000))
-- Converts a comman separated value into a table variable

RETURNS @tbl TABLE( [Value] VARCHAR( 100) COLLATE DATABASE_DEFAULT NOT NULL)

AS BEGIN

DECLARE @Text VARCHAR( 100)

SET @CsvString = RTRIM( LTRIM( @CsvString))
SET @CsvString = REPLACE( @CsvString, CHAR( 9), '')
SET @CsvString = REPLACE( @CsvString, CHAR( 10), '')
SET @CsvString = REPLACE( @CsvString, CHAR( 13), '')

IF LEN( @CsvString) < 1 RETURN

WHILE LEN( @CsvString) > 0 BEGIN

    IF CHARINDEX( ',', @CsvString) > 0 BEGIN

        SET @Text = LEFT( @CsvString, CHARINDEX( ',', @CsvString) - 1)
        SET @CsvString = LTRIM( RTRIM( RIGHT( @CsvString, LEN( @CsvString) - CHARINDEX( ',', @CsvString))))
    END
    ELSE BEGIN

        SET @Text = @CsvString
        SET @CsvString = ''
    END

    INSERT @tbl VALUES( LTRIM( RTRIM( @Text)))
END

RETURN
END

You can then union the two tables together, like so...

SELECT * FROM udfCsvToTable('a,b,c,d')
UNION   
SELECT * FROM udfCsvToTable('c,d,e,f,g')

Which will give you a result set of:

a
b
c
d
e
f
g

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文