如何替换功能性(许多)外部应用程序(选择*从) [英] How to replace a functional (many) OUTER APPLY (SELECT * FROM)

查看:40
本文介绍了如何替换功能性(许多)外部应用程序(选择*从)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

适用于Microsoft SQL Server 2008 R2.

如果我们有几十个Outer Apply(30),则它们开始缓慢运行.在Outer Apply的中间,我有一个比简单的选择视图还要复杂的东西.

If we have a few dozen Outer Apply (30) then they begin to work pretty slowly. In the middle of the Outer Apply I have something more complicated than a simple select, a view.

我正在写一种分配给表的属性(在数据库中).通常,一些表包含对属性表(键,值)的引用.

I'm writing a sort of attributes assigned to tables (in the database). Generally, a few tables, holds a reference to a table of attributes (key, value).

伪结构看起来像这样:

DECLARE @Lot TABLE (
LotId INT PRIMARY KEY IDENTITY, 
SomeText VARCHAR(8))

INSERT INTO @Lot
OUTPUT INSERTED.*
VALUES ('Hello'), ('World')

DECLARE @Attribute TABLE(
AttributeId INT PRIMARY KEY IDENTITY, 
LotId INT, 
Val VARCHAR(8),
Kind VARCHAR(8))

INSERT INTO @Attribute
OUTPUT INSERTED.* VALUES 
(1, 'Foo1', 'Kind1'), (1, 'Foo2', 'Kind2'), 
(2, 'Bar1', 'Kind1'), (2, 'Bar2', 'Kind2'), (2, 'Bar3', 'Kind3')

LotId       SomeText
----------- --------
1           Hello
2           World

AttributeId LotId       Val      Kind
----------- ----------- -------- --------
1           1           Foo1     Kind1
2           1           Foo2     Kind2
3           2           Bar1     Kind1
4           2           Bar2     Kind2
5           2           Bar3     Kind3

我现在可以运行查询,例如:

I can now run a query such as:

SELECT 
[l].[LotId]
  , [SomeText]
  , [Oa1].[AttributeId]
  , [Oa1].[LotId]
  , 'Kind1Val' = [Oa1].[Val]
  , [Oa1].[Kind]
  , [Oa2].[AttributeId]
  , [Oa2].[LotId]
  , 'Kind2Val' = [Oa2].[Val]
  , [Oa2].[Kind]
  , [Oa3].[AttributeId]
  , [Oa3].[LotId]
  , 'Kind3Val' = [Oa3].[Val]
  , [Oa3].[Kind]  
FROM @Lot AS l
OUTER APPLY(SELECT * FROM @Attribute AS la WHERE la.[LotId] = l.[LotId] AND la.[Kind] = 'Kind1') AS Oa1
OUTER APPLY(SELECT * FROM @Attribute AS la WHERE la.[LotId] = l.[LotId] AND la.[Kind] = 'Kind2') AS Oa2
OUTER APPLY(SELECT * FROM @Attribute AS la WHERE la.[LotId] = l.[LotId] AND la.[Kind] = 'Kind3') AS Oa3


LotId       SomeText AttributeId LotId       Kind1Val Kind     AttributeId LotId       Kind2Val Kind     AttributeId LotId       Kind3Val Kind
----------- -------- ----------- ----------- -------- -------- ----------- ----------- -------- -------- ----------- ----------- -------- --------
1           Hello    1           1           Foo1     Kind1    2           1           Foo2     Kind2    NULL        NULL        NULL     NULL
2           World    3           2           Bar1     Kind1    4           2           Bar2     Kind2    5           2           Bar3     Kind3

一种简单的方法来获取属性值的数据透视表和不具有诸如Kind3之类的属性的行的结果. 我知道Microsoft PIVOT ,它是不简单,也不适合这里.

The simple way to get the pivot table of attribute values ​​and results for Lot rows that do not have attribute such a Kind3. I know Microsoft PIVOT and it is not simple and do not fits here.

最后,什么会更快,并且给出相同的结果?

Finally, what will be faster and will give the same results?

推荐答案

要获取结果,您可以先取消透视,然后透视数据.

In order to get the result you can unpivot and then pivot the data.

您可以通过两种方式执行此操作.首先,您可以使用UNPIVOTPIVOT函数:

There are two ways that you can perform this. First, you can use the UNPIVOT and the PIVOT function:

select *
from
(
    select LotId,
        SomeText,
        col+'_'+CAST(rn as varchar(10)) col,
        value
    from
    (
        select l.LotId, 
            l.SomeText,
            cast(a.AttributeId as varchar(8)) attributeid,
            cast(a.LotId as varchar(8)) a_LotId,
            a.Val,
            a.Kind,
            ROW_NUMBER() over(partition by l.lotid order by a.attributeid) rn
        from @Lot l
        left join @Attribute a
            on l.LotId = a.LotId
    ) src
    unpivot
    (
        value
        for col in (attributeid, a_Lotid, val, kind)
    ) unpiv
) d
pivot
(
    max(value)
    for col in (attributeid_1, a_LotId_1, Val_1, Kind_1,
                attributeid_2, a_LotId_2, Val_2, Kind_2,
                attributeid_3, a_LotId_3, Val_3, Kind_3)
) piv

请参见带有演示的SQL小提琴.

或者从SQL Server 2008+开始,可以将CROSS APPLYVALUES子句一起使用来取消数据透视:

Or starting in SQL Server 2008+, you can use CROSS APPLY with a VALUES clause to unpivot the data:

select *
from
(
    select LotId,
        SomeText,
        col+'_'+CAST(rn as varchar(10)) col,
        value
    from
    (
        select l.LotId, 
            l.SomeText,
            cast(a.AttributeId as varchar(8)) attributeid,
            cast(a.LotId as varchar(8)) a_LotId,
            a.Val,
            a.Kind,
            ROW_NUMBER() over(partition by l.lotid order by a.attributeid) rn
        from @Lot l
        left join @Attribute a
            on l.LotId = a.LotId
    ) src
    cross apply
    (
        values ('attributeid', attributeid),('LotId', a_LotId), ('Value', Val), ('Kind', Kind)
    ) c (col, value)
) d
pivot
(
    max(value)
    for col in (attributeid_1, LotId_1, Value_1, Kind_1,
                attributeid_2, LotId_2, Value_2, Kind_2,
                attributeid_3, LotId_3, Value_3, Kind_3)
) piv

请参见带有演示的SQL小提琴.

unpivot进程为每个LotIDSomeText占用多个列,并将其转换为行,从而得到结果:

The unpivot process takes the multiple columns for each LotID and SomeText and converts it into rows giving the result:

| LOTID | SOMETEXT |           COL | VALUE |
--------------------------------------------
|     1 |    Hello | attributeid_1 |     1 |
|     1 |    Hello |       LotId_1 |     1 |
|     1 |    Hello |       Value_1 |  Foo1 |
|     1 |    Hello |        Kind_1 | Kind1 |
|     1 |    Hello | attributeid_2 |     2 |

我在内部子查询中添加了row_number(),用于创建新的要旋转的列名.创建名称后,即可将数据透视表应用于新列,从而获得最终结果

I added a row_number() to the inner subquery to be used to create the new column names to pivot. Once the names are created the pivot can be applied to the new columns giving the final result

这也可以使用动态SQL来完成:

This could also be done using dynamic SQL:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(col+'_'+rn) 
                    from 
                    (
                      select 
                        cast(ROW_NUMBER() over(partition by l.lotid order by a.attributeid) as varchar(10)) rn
                      from Lot l
                      left join Attribute a
                          on l.LotId = a.LotId
                    ) t
                    cross apply (values ('attributeid', 1),
                                 ('LotId', 2), 
                                 ('Value', 3), 
                                 ('Kind', 4)) c (col, so)
                    group by col, rn, so
                    order by rn, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = 'SELECT LotId,
                    SomeText,' + @cols + ' 
             from 
             (
                select LotId,
                    SomeText,
                    col+''_''+CAST(rn as varchar(10)) col,
                    value
                from
                (
                    select l.LotId, 
                        l.SomeText,
                        cast(a.AttributeId as varchar(8)) attributeid,
                        cast(a.LotId as varchar(8)) a_LotId,
                        a.Val,
                        a.Kind,
                        ROW_NUMBER() over(partition by l.lotid order by a.attributeid) rn
                    from Lot l
                    left join Attribute a
                        on l.LotId = a.LotId
                ) src
                cross apply
                (
                    values (''attributeid'', attributeid),(''LotId'', a_LotId), (''Value'', Val), (''Kind'', Kind)
                ) c (col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

execute(@query)

请参见带有演示的SQL小提琴

所有三个版本将给出相同的结果:

All three versions will give the same result:

| LOTID | SOMETEXT | ATTRIBUTEID_1 | LOTID_1 | VALUE_1 | KIND_1 | ATTRIBUTEID_2 | LOTID_2 | VALUE_2 | KIND_2 | ATTRIBUTEID_3 | LOTID_3 | VALUE_3 | KIND_3 |
-----------------------------------------------------------------------------------------------------------------------------------------------------------
|     1 |    Hello |             1 |       1 |    Foo1 |  Kind1 |             2 |       1 |    Foo2 |  Kind2 |        (null) |  (null) |  (null) | (null) |
|     2 |    World |             3 |       2 |    Bar1 |  Kind1 |             4 |       2 |    Bar2 |  Kind2 |             5 |       2 |    Bar3 |  Kind3 |

这篇关于如何替换功能性(许多)外部应用程序(选择*从)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆