Splitting Rows Into Columns

  • Hello, Thanks for checking post, below is the table script and output required. Below i have provided only 4 columns, table is having more than 100 columns.  If there is no difference between 2 rows based on Ldate then we can ignore. Row 2 and Row 3 having differences in last name. So i need both of them next to next.

     

    CREATE TABLE #TEMP
    (
    TID VARCHAR(100),
    FName VARCHAR(100),
    LName VARCHAR(100),
    LDate DATE
    )

    INSERT INTO #TEMP VALUES ('878A','user1','user2','01/01/2019') -Row1
    INSERT INTO #TEMP VALUES ('878A','user1','user2','01/03/2019') -Row2
    INSERT INTO #TEMP VALUES ('878A','user1','user3','01/04/2019') -Row3

    ------OUTPUT
    COLUMNS 01/03/2019 01/04/2019
    TID 878A 878A
    FName User1 User1
    LName User2 User3

    Please let me know if any questions.

    • This topic was modified 5 years ago by  koti.raavi.
    • This topic was modified 5 years ago by  koti.raavi. Reason: Spelling correction
    • This topic was modified 5 years ago by  koti.raavi.
  • Your desired output doesn't seem right. There is no 'user2' in FName column, there is no 'user1' in LName. How come they appear in respecting output rows. It's a crosstab query you're after, right?

    --Vadim R.

  • Good Catch 🙂 I have updated code now, Yes i need cross tab query. Please let me know if any questions.

  • Here is one way doing this:

    SELECT 
    [COLUMNS] = 'TID',
    [2019-01-01] = MIN(CASE WHEN LDate = '20190101' THEN TID END),
    [2019-01-03] = MIN(CASE WHEN LDate = '20190103' THEN TID END),
    [2019-01-04] = MIN(CASE WHEN LDate = '20190104' THEN TID END)
    FROM #TEMP
    GROUP BY TID

    UNION ALL

    SELECT
    [COLUMNS] = 'FName',
    [2019-01-01] = MIN(CASE WHEN LDate = '20190101' THEN FName END),
    [2019-01-03] = MIN(CASE WHEN LDate = '20190103' THEN FName END),
    [2019-01-04] = MIN(CASE WHEN LDate = '20190104' THEN FName END)
    FROM #TEMP
    GROUP BY TID

    UNION ALL

    SELECT
    [COLUMNS] = 'LName',
    [2019-01-01] = MIN(CASE WHEN LDate = '20190101' THEN LName END),
    [2019-01-03] = MIN(CASE WHEN LDate = '20190103' THEN LName END),
    [2019-01-04] = MIN(CASE WHEN LDate = '20190104' THEN LName END)
    FROM #TEMP
    GROUP BY TID

    Also, look at PIVOT operator: https://docs.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-2017

    Also, check SQL# library, it has some splitting functions like String_SplitIntoFields.

    --Vadim R.

  • I'm guessing you're wanting something a bit more dynamic here?

    Thom~

    Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
    Larnu.uk

  • You got it Thom, That's correct, Am looking for dynamic, my table contains more than 100 columns and also 90-92 rows for Each ID -  EX: table contains 3months data and everyday we load same ID regardless of change/no change..and now we need change report in place. I know we could of store records which are having mismatch - but design already in place.

  • Sure, if number of columns is unpredictable you'll need dynamic SQL. But inside that dynamic SQL you may implement one of the crosstab techniques. The only other way I can think of is to dynamically create crosstab temp table based on current data and populate it in the loop and/or series of Inserts/Updates from source table.

    --Vadim R.

  • OK, well, here's the FULLY dynamic answer. This'll handle additional columns to your table, and additional dates. It's ugly, but it works. Is this a good idea, certainly not, but I "enjoy" (is that the right word) a dynamic SQL challenge. Note, this assumes that your date s (like '01/01/2019') are in the format dd/MM/yyyy. If not, you will need to change any style code references of 103 to 101. Also note I use a real table, not a temporary table (that is important):

    USE Sandbox;
    GO

    CREATE TABLE TEMP
    (
    TID VARCHAR(100),
    FName VARCHAR(100),
    LName VARCHAR(100),
    LDate DATE
    )
    INSERT INTO TEMP VALUES ('878A','user1','user2','20190101')
    INSERT INTO TEMP VALUES ('878A','user1','user2','20190301')
    INSERT INTO TEMP VALUES ('878A','user1','user3','20190401')

    GO
    --SELECT *
    --FROM TEMP;

    GO
    DECLARE @SQL nvarchar(MAX);

    SET @SQL = N'SELECT V.COLUMNS,' + NCHAR(13) + NCHAR(10) +
    STUFF((SELECT N',' + NCHAR(13) + NCHAR(10) +
    N' MAX(CASE T.LDATE WHEN ' + QUOTENAME(CONVERT(varchar(8),T.LDATE,112),'''') + N' THEN CASE V.COLUMNS ' +
    STUFF((SELECT NCHAR(13) + NCHAR(10) +
    N' WHEN N' + QUOTENAME(C.COLUMN_NAME,'''') + N' THEN CONVERT(varchar(100),' + QUOTENAME(C.COLUMN_NAME) + N')'
    FROM INFORMATION_SCHEMA.COLUMNS C
    WHERE TABLE_NAME = N'TEMP'
    AND COLUMN_NAME != N'LDATE'
    ORDER BY ORDINAL_POSITION
    FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,62,N'') + NCHAR(13) + NCHAR(10) +
    N' END' + NCHAR(13) + NCHAR(10) +
    N' END) AS ' + QUOTENAME(CONVERT(varchar(10),LDATE,103))
    FROM TEMP T
    GROUP BY LDATE
    FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + NCHAR(13) + NCHAR(10) +
    N'FROM TEMP T' + NCHAR(13) + NCHAR(10) +
    N' CROSS APPLY (VALUES' + STUFF((SELECT N',(N' + QUOTENAME(COLUMN_NAME,'''') + N')'
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_NAME = N'TEMP'
    AND COLUMN_NAME != N'LDATE'
    ORDER BY ORDINAL_POSITION ASC
    FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,1,N'') + N') V(COLUMNS)' + NCHAR(13) + NCHAR(10) +
    N'GROUP BY V.COLUMNS;';

    PRINT @SQL; --Your debugging best friend
    EXEC sp_executesql @SQL;

    GO
    DROP TABLE TEMP;

    The above SQL produces, and executes, the below SQL (with formatting, which is why my SQL looks the way it does):

    SELECT V.COLUMNS,
    MAX(CASE T.LDATE WHEN '20190101' THEN CASE V.COLUMNS WHEN N'TID' THEN [TID]
    WHEN N'FName' THEN [FName]
    WHEN N'LName' THEN [LName]
    END
    END) AS [01/01/2019],
    MAX(CASE T.LDATE WHEN '20190301' THEN CASE V.COLUMNS WHEN N'TID' THEN [TID]
    WHEN N'FName' THEN [FName]
    WHEN N'LName' THEN [LName]
    END
    END) AS [01/03/2019],
    MAX(CASE T.LDATE WHEN '20190401' THEN CASE V.COLUMNS WHEN N'TID' THEN [TID]
    WHEN N'FName' THEN [FName]
    WHEN N'LName' THEN [LName]
    END
    END) AS [01/04/2019]
    FROM TEMP T
    CROSS APPLY (VALUES(N'TID'),(N'FName'),(N'LName')) V(COLUMNS)
    GROUP BY V.COLUMNS;

    And produces the results below:

    COLUMNS  01/01/2019  01/03/2019  01/04/2019
    TID 878A 878A 878A
    FName user1 user1 user1
    LName user2 user2 user3

    But, it's not up to be to understand what this does and how it works, it's up to you koti.raavi. If you don't understand it, I can try to explain some of it, however, that is far from entry level SQL. If you don't understand it in it's entirety use it with caution; I cannot and will not be able to support the above code.

    Edit: Wow, that looks awful on the forums... Just, awful. I suggest pasting that into SSMS, or ADS (Azure Data Studio) to see what the above looks like.

    • This reply was modified 5 years ago by  Thom A.

    Thom~

    Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
    Larnu.uk

  • It does look good, it worked almost 98%, but issue is here with order, the few columns are added to table later - I mean not with original table creation, I cross checked in Information Columns table those are in correct order. but query pulling them in top position .

    Thank you So Much For Help!

     

     

     

     

  • koti.raavi wrote:

    It does look good, it worked almost 98%, but issue is here with order, the few columns are added to table later - I mean not with original table creation, I cross checked in Information Columns table those are in correct order. but query pulling them in top position . Thank you So Much For Help!        

    Not sure what you mean here. The columns are sorted by ordinal position (see the ORDER BY ORDINAL_POSITION ASC clause in one of the statements). New columns in the table TEMP will be added the bottom of the dataset in the dynamic statement.

    I did, however, miss out ORDER BY LDATE after GROUP BY LDATE; perhaps that the problem you're having? I can't see what you see so I'm working blind here.

    Thom~

    Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
    Larnu.uk

  • DB<>Fiddle showing "working as intended".

    Thom~

    Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
    Larnu.uk

Viewing 11 posts - 1 through 10 (of 10 total)

You must be logged in to reply to this topic. Login to reply