简体   繁体   中英

how to find all strings in between commas in sql server

I want to display a string in a table format as shown below:

For a string like 'hi,is,1,question,thanks,.,.,n'

I need this result:

column1    column2  column3   column4  ..... column
hi         is       1         question ..... n

This is not trivial. You will find a lot of examples how to split your string in a set of fragments. And you will find a lot of examples how to pivot a row set to a single row. But - adding quite some difficulty - you have an unknown count of columns. There are three approaches:

  1. Split this and return your set with a known maximum of columns
  2. Use a dynamically created statement and use EXEC . But this will not work in VIEWs or iTVFs, nor will it work against a table.
  3. Instead of a column list you return a generic container like XML

with a known maximum of columns

One example for the first was this

DECLARE @str VARCHAR(1000)='This,is,a,string,with,n,elements,...';

SELECT p.*
FROM
(
    SELECT A.[value]
          ,CONCAT('Column',A.[key]+1) AS ColumnName
    FROM OPENJSON('["' + REPLACE(@str,',','","') + '"]') A
) t
PIVOT
(
    MAX(t.[value]) FOR ColumnName IN(Column1,Column2,Column3,Column4,Column5,Column6,Column7,Column8,Column9 /*add as many as you need*/)
) p

Hint: My approach to split the string uses OPENJSON , not available before version 2016. But there are many other approaches you'll find easily. It's just an example to show you the combination of a splitter with PIVOT using a running index to build up a column name.

Unknown count of columns

And the same example with a dynamically created column list was this:

DECLARE @str VARCHAR(1000)='This,is,a,string,with,n,elements,...';

DECLARE @CountElements INT=LEN(@str)-LEN(REPLACE(@str,',',''))+1;
DECLARE @columnList NVARCHAR(MAX)=
STUFF((
    SELECT TOP(@CountElements) 
           CONCAT(',Column',ROW_NUMBER() OVER(ORDER BY (SELECT 1))) 
    FROM master..spt_values /*has a lot of rows*/
    FOR XML PATH('')
),1,1,'');

DECLARE @Command NVARCHAR(MAX)=
N'SELECT p.*
FROM
(
    SELECT A.[value]
          ,CONCAT(''Column'',A.[key]+1) AS ColumnName
    FROM OPENJSON(''["'' + REPLACE(''' + @str + ''','','',''","'') + ''"]'') A
) t
PIVOT
(
    MAX(t.[value]) FOR ColumnName IN(' +  @columnList + ')
) p;';

EXEC(@Command);

Hint: The statement created is exactly the same as above. But the column list in the pivot's IN is created dynamically. This will work with (almost) any count of words generically.

If you need more help, please use the edit option of your question and provide some more details.

An inlineable approach for a table returning a generic container

If you need this against a table, you might try something along this:

DECLARE @tbl TABLE(ID INT IDENTITY,YourList NVARCHAR(MAX));
INSERT INTO @tbl VALUES('This,is,a,string,with,n,elements,...')
                      ,('And,one,more');

SELECT *
      ,CAST('<x>' + REPLACE((SELECT t.YourList AS [*] FOR XML PATH('')),',','</x><x>') + '</x>' AS XML) AS Splitted
FROM @tbl t

This will return your list as an XML like

<x>This</x>
<x>is</x>
<x>a</x>
<x>string</x>
<x>with</x>
<x>n</x>
<x>elements</x>
<x>...</x>

You can grab - if needed - each element by its index like here

TheXml.value('/x[1]','nvarchar(max)') AS Element1
DECLARE @string VARCHAR(MAX);
SET @string = 'hi,is,1,question,thanks,.,.,n';

DECLARE @SQL VARCHAR(MAX);
SET @SQL = 'SELECT ''' + REPLACE(@string, ',', ''',''') + '''';

EXEC (@SQL);

Result: 在此处输入图片说明

  1. Add SELECT ' at beginning and ' at the end of string

  2. Replace all , with ',' inside string

  3. So string 'hi,is,1,question,thanks,.,.,n' is replace by ' SELECT 'hi','is','1','question','thanks','.','.','n' '

在此处输入图片说明

  1. Executed as SQL query

PS: If you want to use it on column you will have to combine it with CURSOR

Update

    DECLARE @table TABLE
(
    ID INT IDENTITY,
    string VARCHAR(MAX)
);
INSERT INTO @table
VALUES
('This,is,a,string,,n,elements,..');
INSERT INTO @table
VALUES
('And,one,more');
INSERT INTO @table
VALUES
('Ugly,but,works,,,Yay!,..,,,10,11,12,13,14,15,16,17,18,19,..');

SELECT * FROM @table 

DECLARE @string_to_split VARCHAR(MAX);
DECLARE @sql_query_to_execute VARCHAR(MAX);
DECLARE @max_elements INT, @id INT, @i INT;

SET @i = 1;

DECLARE string_cursor CURSOR FOR SELECT ID, string FROM @table;

SELECT @max_elements = MAX(LEN(string) - LEN(REPLACE(string, ',', ''))) + 1 -- Find  max  number of elements */
FROM @table;

IF OBJECT_ID('tempdb..##my_temp_table_for_splitted_columns') <> 0 -- Create new temp table with valid amount of columns  
    DROP TABLE ##my_temp_table_for_splited_columns;

SET @sql_query_to_execute = 'create table ##my_temp_table_for_splitted_columns ( ID int,';  
WHILE @i <= @max_elements
BEGIN
    SET @sql_query_to_execute = @sql_query_to_execute + ' Col' + CAST(@i AS VARCHAR(max)) + ' varchar(25), ';
    SET @i = @i + 1;
END;

SELECT @sql_query_to_execute = SUBSTRING(@sql_query_to_execute, 1, LEN(@sql_query_to_execute) - 1) + ')';
EXEC (@sql_query_to_execute);

/* Split string for each row */
OPEN string_cursor;
FETCH NEXT FROM string_cursor
INTO @id,
     @string_to_split

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @i = MAX(LEN(@string_to_split) - LEN(REPLACE(@string_to_split, ',', ''))) + 1; -- check amount of columns for current string

    WHILE @i < @max_elements 
    BEGIN
        SET @string_to_split = @string_to_split + ','; -- add missing columns
        SET @i = @i + 1;
    END;

    SET @sql_query_to_execute = 'SELECT ' + CAST(@id AS VARCHAR(MAX)) + ',''' + REPLACE(@string_to_split, ',', ''',''') + '''';

    INSERT INTO ##my_temp_table_for_splitted_columns --insert result to temp table
    EXEC (@sql_query_to_execute);

    FETCH NEXT FROM string_cursor
    INTO @id,
         @string_to_split;
END;

CLOSE string_cursor;
DEALLOCATE string_cursor;

SELECT *
FROM ##my_temp_table_for_splitted_columns;

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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