简体   繁体   中英

sql server query - pivot after n columns

I have a table with N Columns:

TABLE:
TB_SAMPLE

COLUMNS:

VALUE_A
COUNT_A
VALUE_B
COUNT_B
VALUE_C
COUNT_C
VALUE_D
COUNT_D
VALUE_E
COUNT_E
VALUE_F
COUNT_F
VALUE_G
COUNT_G
VALUE_H
COUNT_H
VALUE_I
COUNT_I

I would like to return like this:

'Value', 'Count'
A, 1 -- this is the first and the second column
B, 3 -- this is the third and the fourth column
C, 4 -- this is the fifth and sixth column

and so forward. I've tried multiple select's with UNION ALL clause, but as I'm returning a lot of records, it's not so fast. Is there a better way to 'pivot' this table 2 columns?

Thanks.

This can also be performed using CROSS APPLY with a VALUES statement:

SELECT vals, counts
FROM temp t
CROSS APPLY 
(
    VALUES
        (value_a, count_a),
        (value_b, count_b),
        (value_c, count_c),
        (value_d, count_d),
        (value_e, count_e),
        (value_f, count_f),
        (value_g, count_g),
        (value_h, count_h)
) x (vals, counts);

See SQL Fiddle with Demo .

Here is an article to explain how this is done (registration required):

http://www.sqlservercentral.com/articles/CROSS+APPLY+VALUES+UNPIVOT/91234/

You could UNPIVOT, then PIVOT the data, it might perform better:

DECLARE @T TABLE 
(   VALUE_A INT, COUNT_A INT, VALUE_B INT, COUNT_B INT, VALUE_C INT, COUNT_C INT, VALUE_D INT, COUNT_D INT, VALUE_E INT, 
    COUNT_E INT, VALUE_F INT, COUNT_F INT, VALUE_G INT, COUNT_G INT, VALUE_H INT, COUNT_H INT, VALUE_I INT, COUNT_I INT
);
INSERT @T VALUES (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);

WITH D AS
(   SELECT  Category = SUBSTRING(B, 7, LEN(B)), [Type] = 'Value', A
    FROM    @T
            UNPIVOT 
            (   A
                FOR B IN ([VALUE_A], [VALUE_B], [VALUE_C], [VALUE_D], [VALUE_E], [VALUE_F], [VALUE_G], [VALUE_H], [VALUE_I])
            ) upvt
    UNION ALL
    SELECT  Category = SUBSTRING(B, 7, LEN(B)), [Type] = 'Count', A
    FROM    @T
            UNPIVOT 
            (   A
                FOR B IN ([COUNT_A], [COUNT_B], [COUNT_C], [COUNT_D], [COUNT_E], [COUNT_F], [COUNT_G], [COUNT_H], [COUNT_I])
            ) upvt
)
SELECT  *
FROM    D
        PIVOT
        (   SUM(A)
            FOR [Type] IN ([Value], [Count])
        ) pvt;

You could also do this dynamically if required by querying the system views for column names.

ADDENDUM

Just realised this could be achieved with a join rather than a UNION and PIVOT, I'd imagine the join would perform better as it removes the PIVOT operation. I'd still advise testing both though to see which works best with your data.

WITH Val AS
(   SELECT  Category = SUBSTRING(B, 7, LEN(B)), A
    FROM    @T
            UNPIVOT 
            (   A
                FOR B IN ([VALUE_A], [VALUE_B], [VALUE_C], [VALUE_D], [VALUE_E], [VALUE_F], [VALUE_G], [VALUE_H], [VALUE_I])
            ) upvt
), Co AS
(   SELECT  Category = SUBSTRING(B, 7, LEN(B)), A
    FROM    @T
            UNPIVOT 
            (   A
                FOR B IN ([COUNT_A], [COUNT_B], [COUNT_C], [COUNT_D], [COUNT_E], [COUNT_F], [COUNT_G], [COUNT_H], [COUNT_I])
            ) upvt
)
SELECT  val.Category,
        [Value] = val.A,
        [Count] = co.A
FROM    val
        INNER JOIN co
            ON co.Category = val.Category;

Here is another way to do it (if I have understood your question correctly)

declare @table table
(
value_a varchar(10),
count_a int,
value_b varchar(10),
count_b int,
value_c varchar(10),
count_c int,
value_d varchar(10),
count_d int,
value_e varchar(10),
count_e int,
value_f varchar(10),
count_f int,
value_g varchar(10),
count_g int,
value_h varchar(10),
count_h int
)

Insert Into @table
Select  'value a',675,
        'value b',646,
        'value c',13,
        'value d',22,
        'value e',768,
        'value f',223,
        'value g',66,
        'value h',55


SELECT  vals,
        counts
FROM 
   (
    SELECT  *
   FROM @table
    ) p
UNPIVOT
   (vals FOR cols IN 
      ([value_a],[value_b],[value_c],[value_d], [value_e],[value_f],[value_g],[value_h])
)AS unpvtValues
Unpivot 
    (
        counts for counters in ([count_a],[count_b],[count_c], [count_d],[count_e],[count_f],[count_g],[count_h])
    ) as unpvtCounts
where Right(cols, 2) = right(counters, 2)

EDIT

If you want to use some dynamic SQL then you could use this option (This creates a proper table in your DB instead of using a table variable)

create  Table  dbo.TempTable
(
value_a varchar(10),
count_a int,
value_b varchar(10),
count_b int,
value_c varchar(10),
count_c int,
value_d varchar(10),
count_d int,
value_e varchar(10),
count_e int,
value_f varchar(10),
count_f int,
value_g varchar(10),
count_g int,
value_h varchar(10),
count_h int
)

Insert Into dbo.TempTable
Select  'value a',675,
        'value b',646,
        'value c',13,
        'value d',22,
        'value e',768,
        'value f',223,
        'value g',66,
        'value h',55


Declare @ValueCols varchar(max),
        @CountCols varchar(max),
        @sql varchar(max)


Select @ValueCols = Coalesce(@ValueCols + ', ','') + '[' + Column_Name +']'
From INFORMATION_SCHEMA.COLUMNS
Where TABLE_NAME  = 'TempTable'
And COLUMN_NAME Like 'Value%'

Select @CountCols = Coalesce(@CountCols + ', ','') + '[' + Column_Name +']'
From INFORMATION_SCHEMA.COLUMNS
Where TABLE_NAME  = 'TempTable'
And COLUMN_NAME Like 'Count%'


Set @sql = 'SELECT  vals,
        counts
FROM 
   (
    SELECT  *
   FROM dbo.TempTable
    ) p
    UNPIVOT
   (vals FOR cols IN 
      (' + @ValueCols +')
)AS unpvtValues
Unpivot 
    (
        counts for counters in (' + @CountCols + ')
    ) as unpvtCounts
where Right(cols, 2) = right(counters, 2)'


Execute (@sql)

drop table TempTable

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