How to rotate (pivot) the records of a table

1

Good day, Prové with a normal pivot and it does not work for me since the names of the columns must be specified but in this case I do not know if it is a matter of pivot.

How can these results be obtained? Is it possible?

DECLARE @TEMP_INC TABLE (
        INC_ID INT,
        OBJ_ID INT,
        INC_DESCRIPCION VARCHAR(255),
        INC_TIPOOBJ INT,
        INC_FC DATE,
        INC_FM DATE,
        INA_ID INT
        )

INSERT INTO @TEMP_INC (INC_ID, OBJ_ID, INC_DESCRIPCION, INC_TIPOOBJ, INC_FC, INC_FM, INA_ID) 
VALUES
(1439166,   620310, '* descripcion tipo 123',   7,  '2015-07-04 17:07:36.000',  '2015-07-04 17:07:36.000', 8),
(1439167,   620310, '* descripcion tipo 124',   7,  '2015-07-04 17:07:36.000',  '2015-07-04 17:07:36.000', 8),
(1439168,   620311, '* descripcion tipo 125',   7,  '2015-07-04 17:07:37.000',  '2015-07-04 17:07:37.000', 12),
(1439169,   620311, '* descripcion tipo 126',   7,  '2015-07-04 17:07:37.000',  '2015-07-04 17:07:37.000', 15),
(1439170,   620312, '* descripcion tipo 127',   7,  '2015-07-04 17:07:37.000',  '2015-07-04 17:07:37.000', 20),
(1439171,   620312, '* descripcion tipo 128',   7,  '2015-07-04 17:07:37.000',  '2015-07-04 17:07:37.000', 22),
(1439172,   620313, '* descripcion tipo 129',   7,  '2015-07-04 17:07:38.000',  '2015-07-04 17:07:38.000', 27),
(1439173,   620313, '* descripcion tipo 132',   7,  '2015-07-04 17:07:38.000',  '2015-07-04 17:07:38.000', 26),
(1439174,   620314, '* descripcion tipo 133',   7,  '2015-07-04 17:07:39.000',  '2015-07-04 17:07:39.000', 26),
(1439175,   620314, '* descripcion tipo 134',   7,  '2015-07-04 17:07:39.000',  '2015-07-04 17:07:39.000', 24)

SELECT 
        INC_ID,
        OBJ_ID,
        INC_DESCRIPCION,
        INC_TIPOOBJ,
        INC_FC,
        INC_FM,
        INA_ID
FROM @TEMP_INC

1 : Normal Result

2 : Expected result

    
asked by Vulpex 26.07.2017 в 16:52
source

2 answers

1

The following is a solution based on a dynamic query adapted to the data itself, you can use PIVOT but we still have the problem of not wanting to "write" all levels of transposition, so we should always go through a dynamic consultation. This type of queries has its associated problems, lack of engine optimization, security problems, and a limit on the size of the sentence in NVARCHAR(MAX) , for what you are looking for it will surely help you.

First of all we must transform your variable type table to a physical table even if it is temporary so that the execution with sp_executesql can "see" the table. I also added a third level just to test.

SET NOCOUNT ON

CREATE TABLE #TEMP_INC (
        INC_ID INT,
        OBJ_ID INT,
        INC_DESCRIPCION VARCHAR(255),
        INC_TIPOOBJ INT,
        INC_FC DATE,
        INC_FM DATE,
        INA_ID INT,
    NUM INT
        )

INSERT INTO #TEMP_INC (INC_ID, OBJ_ID, INC_DESCRIPCION, INC_TIPOOBJ, INC_FC, INC_FM, INA_ID) 
VALUES
(1439166,   620310, '* descripcion tipo 123',   7,  '2015-07-04 17:07:36.000',  '2015-07-04 17:07:36.000', 8),
(1439167,   620310, '* descripcion tipo 124',   7,  '2015-07-04 17:07:36.000',  '2015-07-04 17:07:36.000', 8),
(1439168,   620311, '* descripcion tipo 125',   7,  '2015-07-04 17:07:37.000',  '2015-07-04 17:07:37.000', 12),
(1439169,   620311, '* descripcion tipo 126',   7,  '2015-07-04 17:07:37.000',  '2015-07-04 17:07:37.000', 15),
(1439170,   620312, '* descripcion tipo 127',   7,  '2015-07-04 17:07:37.000',  '2015-07-04 17:07:37.000', 20),
(1439171,   620312, '* descripcion tipo 128',   7,  '2015-07-04 17:07:37.000',  '2015-07-04 17:07:37.000', 22),
(1439172,   620313, '* descripcion tipo 129',   7,  '2015-07-04 17:07:38.000',  '2015-07-04 17:07:38.000', 27),
(1439173,   620313, '* descripcion tipo 132',   7,  '2015-07-04 17:07:38.000',  '2015-07-04 17:07:38.000', 26),
(1439174,   620314, '* descripcion tipo 133',   7,  '2015-07-04 17:07:39.000',  '2015-07-04 17:07:39.000', 26),
(1439175,   620314, '* descripcion tipo 134',   7,  '2015-07-04 17:07:39.000',  '2015-07-04 17:07:39.000', 24),
(1439176,   620314, '* descripcion tipo 135',   7,  '2015-07-04 17:07:39.000',  '2015-07-04 17:07:40.000', 25)

-- Obtengo el máximo nivel de apertura    
DECLARE @MaxLevels  INT

SELECT  @MaxLevels = MAX(Cantidad)
    FROM (SELECT COUNT(DISTINCT INC_ID) AS 'Cantidad'
            FROM    #TEMP_INC
                GROUP BY OBJ_ID
    ) c


UPDATE  #TEMP_INC
    SET NUM = N.NUM
    FROM #TEMP_INC T
    INNER JOIN (SELECT INC_ID, ROW_NUMBER() OVER (PARTITION BY OBJ_ID ORDER BY OBJ_ID, INC_ID) AS NUM
            FROM #TEMP_INC
        ) N
        ON N.INC_ID = T.INC_ID

DECLARE @SQL    NVARCHAR(MAX)
SELECT  @SQL    = 'SELECT' + CHAR(13)

SELECT  @SQL    = @SQL +'' + 
            '       MAX(CASE WHEN NUM = ' + CONVERT(VARCHAR,N.NUM) + ' THEN INC_ID ELSE NULL END) AS ''INC_ID' + CONVERT(VARCHAR,N.NUM) + ''', ' + CHAR(13) +
            '       MAX(CASE WHEN NUM = ' + CONVERT(VARCHAR,N.NUM) + ' THEN OBJ_ID ELSE NULL END) AS ''OBJ_ID' + CONVERT(VARCHAR,N.NUM) + ''', ' + CHAR(13) +
            '       MAX(CASE WHEN NUM = ' + CONVERT(VARCHAR,N.NUM) + ' THEN INC_DESCRIPCION ELSE NULL END) AS ''INC_DESCRIPCION' + CONVERT(VARCHAR,N.NUM) + ''', ' + CHAR(13) +
            '       MAX(CASE WHEN NUM = ' + CONVERT(VARCHAR,N.NUM) + ' THEN INC_TIPOOBJ ELSE NULL END) AS ''INC_TIPOOBJ' + CONVERT(VARCHAR,N.NUM) + ''', ' + CHAR(13) +
            '       MAX(CASE WHEN NUM = ' + CONVERT(VARCHAR,N.NUM) + ' THEN INC_FC ELSE NULL END) AS ''INC_FC' + CONVERT(VARCHAR,N.NUM) + ''', ' + CHAR(13) +
            '       MAX(CASE WHEN NUM = ' + CONVERT(VARCHAR,N.NUM) + ' THEN INC_FM ELSE NULL END) AS ''INC_FM' + CONVERT(VARCHAR,N.NUM) + ''', ' + CHAR(13) +
            '       MAX(CASE WHEN NUM = ' + CONVERT(VARCHAR,N.NUM) + ' THEN INA_ID ELSE NULL END) AS ''INA_ID' + CONVERT(VARCHAR,N.NUM) + ''', ' + CHAR(13) +
            ''
    FROM ( SELECT   ROW_NUMBER() OVER (ORDER BY ID) AS NUM
            FROM SYSCOLUMNS
    ) N
    WHERE N.NUM <= @MaxLevels

SELECT  @SQL    = LEFT(@SQL, LEN(@SQL)- 3)

SELECT  @SQL    = @SQL + CHAR(13) +
            '       FROM #TEMP_INC' + CHAR(13) +
            '       GROUP BY OBJ_ID'


-- PRINT    @SQL

SET NOCOUNT OFF
EXECUTE SP_EXECUTESQL @SQL

DROP TABLE #TEMP_INC

Explanation

  • We obtain the highest possible level of openness, that is, how many columns * levels we are going to "transpose" ( @MaxLevels )
  • We update a new field NUM that numbers each new record of OBJ_ID and what we will use later
  • We generate the dynamic @SQL by repeating each field by @MaxLevels , we use any table, in my case syscolumns that has many records to generate a sequence, obviously the amount of records must be greater than @MaxLevels
  • We finish completing the sentence and execute it through EXECUTE SP_EXECUTESQL @SQL
  • If you are curious about the final sentence, here is an example

    SELECT
           MAX(CASE WHEN NUM = 1 THEN INC_ID ELSE NULL END) AS 'INC_ID1', 
           MAX(CASE WHEN NUM = 1 THEN OBJ_ID ELSE NULL END) AS 'OBJ_ID1', 
           MAX(CASE WHEN NUM = 1 THEN INC_DESCRIPCION ELSE NULL END) AS 'INC_DESCRIPCION1', 
           MAX(CASE WHEN NUM = 1 THEN INC_TIPOOBJ ELSE NULL END) AS 'INC_TIPOOBJ1', 
           MAX(CASE WHEN NUM = 1 THEN INC_FC ELSE NULL END) AS 'INC_FC1', 
           MAX(CASE WHEN NUM = 1 THEN INC_FM ELSE NULL END) AS 'INC_FM1', 
           MAX(CASE WHEN NUM = 1 THEN INA_ID ELSE NULL END) AS 'INA_ID1', 
           MAX(CASE WHEN NUM = 2 THEN INC_ID ELSE NULL END) AS 'INC_ID2', 
           MAX(CASE WHEN NUM = 2 THEN OBJ_ID ELSE NULL END) AS 'OBJ_ID2', 
           MAX(CASE WHEN NUM = 2 THEN INC_DESCRIPCION ELSE NULL END) AS 'INC_DESCRIPCION2', 
           MAX(CASE WHEN NUM = 2 THEN INC_TIPOOBJ ELSE NULL END) AS 'INC_TIPOOBJ2', 
           MAX(CASE WHEN NUM = 2 THEN INC_FC ELSE NULL END) AS 'INC_FC2', 
           MAX(CASE WHEN NUM = 2 THEN INC_FM ELSE NULL END) AS 'INC_FM2', 
           MAX(CASE WHEN NUM = 2 THEN INA_ID ELSE NULL END) AS 'INA_ID2', 
           MAX(CASE WHEN NUM = 3 THEN INC_ID ELSE NULL END) AS 'INC_ID3', 
           MAX(CASE WHEN NUM = 3 THEN OBJ_ID ELSE NULL END) AS 'OBJ_ID3', 
           MAX(CASE WHEN NUM = 3 THEN INC_DESCRIPCION ELSE NULL END) AS 'INC_DESCRIPCION3', 
           MAX(CASE WHEN NUM = 3 THEN INC_TIPOOBJ ELSE NULL END) AS 'INC_TIPOOBJ3', 
           MAX(CASE WHEN NUM = 3 THEN INC_FC ELSE NULL END) AS 'INC_FC3', 
           MAX(CASE WHEN NUM = 3 THEN INC_FM ELSE NULL END) AS 'INC_FM3', 
           MAX(CASE WHEN NUM = 3 THEN INA_ID ELSE NULL END) AS 'INA_ID3'
           FROM #TEMP_INC
           GROUP BY OBJ_ID
    
        
    answered by 26.07.2017 / 18:18
    source
    1

    Depending on the image of the expected result and the records, you could consider the order of the column INC_ID and equal the value of the column OBJ_ID of the previous one with the current record.

    For example:

    ;WITH TEMP
    AS (SELECT LAG(INC_ID) OVER (ORDER BY INC_ID) [INC_ID_1]
           ,INC_ID [INC_ID_2]
        FROM @TEMP_INC
       )
    SELECT T1.*
       ,T2.*
    FROM TEMP T
        INNER JOIN @TEMP_INC T1
            ON T1.INC_ID = T.INC_ID_1
        INNER JOIN @TEMP_INC T2
            ON T2.INC_ID = T.INC_ID_2
    WHERE T1.OBJ_ID = T2.OBJ_ID
    

    I take advantage of the LAG feature that is available to start of SQL Server 2012; for other versions it would be playing with the sequence of each record.

        
    answered by 26.07.2017 в 19:16