0

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
Nick
  • 138,499
  • 22
  • 57
  • 95
manoj
  • 31
  • 1
  • 5
  • I mean comma separated as a column , each string in between two commas as one column – manoj Feb 20 '19 at 07:11
  • And please edit your question to add further information - not add comments. And familiarise yourself with the formatting options available to make your question easy to follow. – Dale K Feb 20 '19 at 07:12
  • Consider https://coderwall.com/p/zzgo-w/splitting-strings-with-mysql – ycx Feb 20 '19 at 07:14
  • 3
    Possible duplicate of [How to split a comma-separated value to columns](https://stackoverflow.com/questions/10581772/how-to-split-a-comma-separated-value-to-columns) – JuanN Feb 20 '19 at 07:16
  • You tagged this with `mysql`, but the title says `sql-server`. Please be as precise as possible. The answers may vary widely depending on the actual RDBMS. – Shnugo Feb 20 '19 at 07:26
  • 1
    What if first row contains 2 commas and second one contains 200,000? – Salman A Feb 20 '19 at 07:31
  • You can't do this. The number of columns a query returns must be know to the engine **before** the query is actually run. Plus a query can't return a different number of columns for each row. If at all you need to set an upper limit for the number of columns, e.g. because you know there will never be more than 20 words in the string. –  Feb 20 '19 at 07:36
  • There are types built into SQL Server *designed* for holding multiple values. In decreasing order of my own preferences, these would be table-valued parameters/values, XML or JSON. Why are you using such an inappropriate data type to start with? – Damien_The_Unbeliever Feb 20 '19 at 08:01
  • @Damien_The_Unbeliever In Sql Server, Json is not a data type - it's simply a well-formatted string. The fact that it's well-formatted means SQL Server can have built in functions to handle json data - but it's still just a string, and [should be stored in an `nvarchar` column](https://learn.microsoft.com/en-us/sql/relational-databases/json/store-json-documents-in-sql-tables?view=sql-server-2017) – Zohar Peled Feb 21 '19 at 14:12

2 Answers2

1

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
Community
  • 1
  • 1
Shnugo
  • 66,100
  • 9
  • 53
  • 114
1
DECLARE @string VARCHAR(MAX);
SET @string = 'hi,is,1,question,thanks,.,.,n';

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

EXEC (@SQL);

Result: enter image description here

  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''

enter image description here

  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;

enter image description here

  • THANKS @slawomir , it working , can you please let me know how it works , I mean behind logic ? any way thanks ............. – manoj Feb 21 '19 at 07:27
  • thanks,got it @slawomir, can we implement in table level?, I mean in string place one column name from table – manoj Feb 21 '19 at 11:06