Конкатенация строк в SQL-сервере - PullRequest
0 голосов
/ 24 февраля 2012

Рассмотрим ситуацию, когда у нас есть две переменные в SP SQL Server 2005, как показано ниже,

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

Есть ли решение для получения новой строки из этого типа (@ string1 U @ string2) без использования каких-либо циклов. то есть последняя строка должна выглядеть так:

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

Ответы [ 5 ]

1 голос
/ 24 февраля 2012

Если вам нужно сделать это как набор, а не по одной строке за раз. Учитывая следующую функцию разбиения:

USE tempdb;
GO
CREATE FUNCTION dbo.SplitStrings(@List NVARCHAR(MAX))
RETURNS TABLE
AS
   RETURN ( SELECT Item FROM
       ( SELECT Item = x.i.value('(./text())[1]', '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 DISTINCT ID, Items = STUFF((SELECT ',' + Item 
    FROM y AS y2 WHERE y2.ID = y.ID 
    FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 1, N'')
FROM y;

Результаты:

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

Теперь, когда все сказали, что вы действительно должны сделать, это следовать предыдущему совету и сохранить эти вещи в связанной таблице в первую очередь. Вы можете использовать методологию разделения одного и того же типа для хранения строк по отдельности всякий раз, когда происходит вставка или обновление, вместо того, чтобы просто выгружать CSV в один столбец, и вашим приложениям на самом деле не нужно менять способ передачи данных в ваши процедуры. Но, конечно, будет проще получить данные!

EDIT

Добавление потенциального решения для 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 sp_executesql @sql;
GO
DROP TABLE #x;

Это гораздо сложнее сделать в 2005 году (хотя и не невозможно), потому что вам нужно изменить предложения VALUES() на UNION ALL ...

1 голос
/ 24 февраля 2012

Найдено эта функция dbo.Split в связанном ответе , который вы можете использовать следующим образом:

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
1 голос
/ 24 февраля 2012

Это можно сделать двумя способами:

  • Создайте функцию CLR, чтобы выполнить работу за вас.Переместите логику обратно в код .NET, который является гораздо более простой платформой для работы со строками.

  • Если вам нужно использовать SQL Server, вам потребуется:

"разбить" две строки на две таблицы, эта функция может помочь: http://blog.logiclabz.com/sql-server/split-function-in-sql-server-to-break-comma-separated-strings-into-table.aspx

Получить уникальный список строк из двух таблиц.(простой запрос)

"взорвать" две строковые таблицы в переменную (/108903/kak-obedinit-tekst-iz-neskolkih-strok-v-odnu-tekstovuy-stroku-na-servere-sql)

1 голос
/ 24 февраля 2012

Как насчет

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

Извините, не ясно, вы хотели только уникальные события. Какую версию SQL-сервера вы используете? Функции манипуляции со строками варьируются в зависимости от версии.

Если вы не против 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)
0 голосов
/ 24 февраля 2012

Следующая функция 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')

Что даст вам набор результатов:

а б с d е е г

...