SQL Server UNPIVOT表由两行基于条件组合而成,使用单个非主元。

时间:2021-04-28 10:20:50

I have a big query which gives a table result looking like this:

我有一个很大的查询,结果是这样的:

[Date]          [Type]  TC1   TC2    TC3
..........................................
'2014-01-01'    T       11    22     33
'2014-01-01'    C       44    55     66
'2014-01-02'    T       111   222    333
'2014-01-02'    C       444   555    666
'2014-01-03'    T       1111  2222   3333
'2014-01-04'    C       4444  5555   6666

I want to make it look like this:

我想让它看起来像这样:

[Date]          Cntr      TValue      CValue
............................................
'2014-01-01'    TC1       11          44
'2014-01-01'    TC2       22          55
'2014-01-01'    TC3       33          66
'2014-01-02'    TC1       111         444
'2014-01-02'    TC2       222         555
'2014-01-02'    TC3       333         666
'2014-01-03'    TC1       1111        4444
'2014-01-03'    TC2       2222        5555
'2014-01-03'    TC3       3333        6666

I managed to do this by unpivoting two times and then join the results. The query I made is this:

我试着去旋转两次,然后加入结果。我的查询是这样的:

SELECT A.[Date],A.Cntr,A.TValue,B.CValue
FROM
(
    SELECT [Date],Cntr,TValue
    FROM TB 
    UNPIVOT
    (
    TValue
    FOR Cntr IN (TC1,TC2,TC3)
    ) u
    WHERE  [Type] = 'T' AND u.[Type] = 'T'
) A
JOIN
(
    SELECT [Date],Cntr,CValue
    FROM TB 
    UNPIVOT
    (
    CValue
    FOR Cntr IN (TC1,TC2,TC3)
    ) u
    WHERE  [Type] = 'C' AND u.[Type] = 'C'
) B
ON B.[Date] = A.[Date] AND A.[Cntr] = B.[Cntr]

My problem is that TB table comes from a big and time consuming query and, given the fact that it will be called thousands of times during my stored procedure, I dont want to waste precious time by inserting its many rows in a temporary table.

我的问题是TB表来自一个庞大而耗时的查询,而且考虑到它在存储过程中将被调用数千次,我不想浪费宝贵的时间将它的许多行插入临时表中。

Is there a way to solve this by using a single unpivot, or using a better approach ? Also, this has to run on SQLServer >= 2005

是否有一种方法可以通过使用单个unpivot或者使用更好的方法来解决这个问题?而且,这必须在SQLServer >= 2005上运行

SqlFiddle here

SqlFiddle这里

Thank you!

谢谢你!

2 个解决方案

#1


3  

Since (Date/Type) are constrained to be unique in TB, then you can UNPIVOT and then PIVOT to avoid the join, MAX(Value) is an arbitrary aggregation, since there can only be one value anyway.

由于(Date/Type)在TB中被限制为惟一的,因此您可以取消PIVOT并转向以避免连接,MAX(Value)是一个任意的聚合,因为无论如何只能有一个值。

SELECT  pvt.Date,
        pvt.Cntr,
        TValue = pvt.T,
        CValue = pvt.C
FROM    TB
        UNPIVOT (Value FOR Cntr IN (TC1, TC2, TC3)) AS upvt
        PIVOT (MAX(Value) FOR [Type] IN (C, T)) AS pvt;

Example on SQL Fiddle

示例SQL小提琴

This results in half the number of reads (as you'd probably expect). IO Statistics show:

这将导致半数的读取(正如您可能预期的那样)。IO统计数据显示:

UNPIVOT/PIVOT

透视/主

Table 'TB'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

表“结核”。扫描计数1,逻辑读取1,物理读取0,读前读取0,lob逻辑读取0,lob物理读取0,lob读取前读取0。

UNPIVOT/UNPIVOT/JOIN

透视/透视/加入

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

表“工作台”。扫描计数0,逻辑读取0,物理读取0,读前读取0,lob逻辑读取0,lob物理读取0,lob读取前读取0。

Table 'TB'. Scan count 2, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

表“结核”。扫描数2,逻辑读2,物理读0,读读0,lob逻辑读0,lob物理读0,lob读读0。

And a simpler execution plan (UNPIVOT/PIVOT on top):

和一个更简单的执行计划(上面的UNPIVOT/PIVOT):

SQL Server UNPIVOT表由两行基于条件组合而成,使用单个非主元。

#2


1  

One way to do this is using cross apply and conditional aggregation:

一种方法是使用交叉应用和条件聚合:

select [date], [cntr],
       max(case when [type] = 'T' then val end) as TValue,
       max(case when [type] = 'C' then val end) as CValue
from (<your query here>) t cross apply
     (values ([date], [type], 'tc1', tc1),
             ([date], [type], 'tc2', tc2),
             ([date], [type], 'tc3', tc3)
     ) vals([date], [type], cntr, val)
group by [date], [cntr];

EDIT:

编辑:

I missed the SQL Server 2005 condition. The syntax before values() is:

我错过了SQL Server 2005的条件。值()之前的语法是:

select [date], [cntr],
       max(case when [type] = 'T' then val end) as TValue,
       max(case when [type] = 'C' then val end) as CValue
from (<your query here>) t cross apply
     (select [date], [type], 'tc1', tc1 union all
      select [date], [type], 'tc2', tc2 union all
      select [date], [type], 'tc3', tc3
     ) vals([date], [type], cntr, val)
group by [date], [cntr];

#1


3  

Since (Date/Type) are constrained to be unique in TB, then you can UNPIVOT and then PIVOT to avoid the join, MAX(Value) is an arbitrary aggregation, since there can only be one value anyway.

由于(Date/Type)在TB中被限制为惟一的,因此您可以取消PIVOT并转向以避免连接,MAX(Value)是一个任意的聚合,因为无论如何只能有一个值。

SELECT  pvt.Date,
        pvt.Cntr,
        TValue = pvt.T,
        CValue = pvt.C
FROM    TB
        UNPIVOT (Value FOR Cntr IN (TC1, TC2, TC3)) AS upvt
        PIVOT (MAX(Value) FOR [Type] IN (C, T)) AS pvt;

Example on SQL Fiddle

示例SQL小提琴

This results in half the number of reads (as you'd probably expect). IO Statistics show:

这将导致半数的读取(正如您可能预期的那样)。IO统计数据显示:

UNPIVOT/PIVOT

透视/主

Table 'TB'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

表“结核”。扫描计数1,逻辑读取1,物理读取0,读前读取0,lob逻辑读取0,lob物理读取0,lob读取前读取0。

UNPIVOT/UNPIVOT/JOIN

透视/透视/加入

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

表“工作台”。扫描计数0,逻辑读取0,物理读取0,读前读取0,lob逻辑读取0,lob物理读取0,lob读取前读取0。

Table 'TB'. Scan count 2, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

表“结核”。扫描数2,逻辑读2,物理读0,读读0,lob逻辑读0,lob物理读0,lob读读0。

And a simpler execution plan (UNPIVOT/PIVOT on top):

和一个更简单的执行计划(上面的UNPIVOT/PIVOT):

SQL Server UNPIVOT表由两行基于条件组合而成,使用单个非主元。

#2


1  

One way to do this is using cross apply and conditional aggregation:

一种方法是使用交叉应用和条件聚合:

select [date], [cntr],
       max(case when [type] = 'T' then val end) as TValue,
       max(case when [type] = 'C' then val end) as CValue
from (<your query here>) t cross apply
     (values ([date], [type], 'tc1', tc1),
             ([date], [type], 'tc2', tc2),
             ([date], [type], 'tc3', tc3)
     ) vals([date], [type], cntr, val)
group by [date], [cntr];

EDIT:

编辑:

I missed the SQL Server 2005 condition. The syntax before values() is:

我错过了SQL Server 2005的条件。值()之前的语法是:

select [date], [cntr],
       max(case when [type] = 'T' then val end) as TValue,
       max(case when [type] = 'C' then val end) as CValue
from (<your query here>) t cross apply
     (select [date], [type], 'tc1', tc1 union all
      select [date], [type], 'tc2', tc2 union all
      select [date], [type], 'tc3', tc3
     ) vals([date], [type], cntr, val)
group by [date], [cntr];