什么时候应该使用交叉套用内部联结?

使用CROSS APPLY的主要目的是什么?

我已经阅读(隐约地通过互联网上的帖子),如果您正在进行分区,那么在选择大型数据集时, cross apply可以更有效。 (寻呼可想而知)

我也知道CROSS APPLY不需要UDF作为右表。

在大多数INNER JOIN查询(一对多关系)中,我可以重写它们以使用CROSS APPLY ,但它们总是给我等效的执行计划。

任何人都可以给我一个很好的例子,说明什么时候CROSS APPLYINNER JOIN工作的情况下会INNER JOIN


编辑:

以下是一个简单的例子,执行计划完全相同。 (向我展示一个他们不同的地方, cross apply的地方更快/更高效)

create table Company (
    companyId int identity(1,1)
,   companyName varchar(100)
,   zipcode varchar(10) 
,   constraint PK_Company primary key (companyId)
)
GO

create table Person (
    personId int identity(1,1)
,   personName varchar(100)
,   companyId int
,   constraint FK_Person_CompanyId foreign key (companyId) references dbo.Company(companyId)
,   constraint PK_Person primary key (personId)
)
GO

insert Company
select 'ABC Company', '19808' union
select 'XYZ Company', '08534' union
select '123 Company', '10016'


insert Person
select 'Alan', 1 union
select 'Bobby', 1 union
select 'Chris', 1 union
select 'Xavier', 2 union
select 'Yoshi', 2 union
select 'Zambrano', 2 union
select 'Player 1', 3 union
select 'Player 2', 3 union
select 'Player 3', 3 


/* using CROSS APPLY */
select *
from Person p
cross apply (
    select *
    from Company c
    where p.companyid = c.companyId
) Czip

/* the equivalent query using INNER JOIN */
select *
from Person p
inner join Company c on p.companyid = c.companyId

任何人都可以给我一个很好的例子,说明什么时候CROSS APPLY在INNER JOIN工作的情况下会有所作为?

有关详细的性能比较,请参阅我博客中的文章:

  • INNER JOINCROSS APPLY
  • CROSS APPLY工程上的事情,有没有简单的更好的JOIN条件。

    这一个选择3从最后一个记录t2从每个记录t1

    SELECT  t1.*, t2o.*
    FROM    t1
    CROSS APPLY
            (
            SELECT  TOP 3 *
            FROM    t2
            WHERE   t2.t1_id = t1.id
            ORDER BY
                    t2.rank DESC
            ) t2o
    

    它不容易用INNER JOIN条件制定。

    你可以用CTE和窗口函数做类似的事情:

    WITH    t2o AS
            (
            SELECT  t2.*, ROW_NUMBER() OVER (PARTITION BY t1_id ORDER BY rank) AS rn
            FROM    t2
            )
    SELECT  t1.*, t2o.*
    FROM    t1
    INNER JOIN
            t2o
    ON      t2o.t1_id = t1.id
            AND t2o.rn <= 3
    

    ,但是这不太可读,可能效率较低。

    更新:

    刚刚检查。

    master是一个包含idPRIMARY KEY的约20,000,000条记录的表。

    这个查询:

    WITH    q AS
            (
            SELECT  *, ROW_NUMBER() OVER (ORDER BY id) AS rn
            FROM    master
            ),
            t AS 
            (
            SELECT  1 AS id
            UNION ALL
            SELECT  2
            )
    SELECT  *
    FROM    t
    JOIN    q
    ON      q.rn <= t.id
    

    运行近30秒,而这一个:

    WITH    t AS 
            (
            SELECT  1 AS id
            UNION ALL
            SELECT  2
            )
    SELECT  *
    FROM    t
    CROSS APPLY
            (
            SELECT  TOP (t.id) m.*
            FROM    master m
            ORDER BY
                    id
            ) q
    

    是即时的。


    cross apply有时可以让你做一些inner join无法做到的事情。

    示例(语法错误):

    select F.* from sys.objects O  
    inner join dbo.myTableFun(O.name) F   
    on F.schema_id= O.schema_id
    

    这是一个语法错误 ,因为与inner join一起使用时,表函数只能将变量或常量用作参数。 (即,表函数参数不能依赖于另一个表的列。)

    然而:

    select F.* from sys.objects O  
    cross apply ( select * from dbo.myTableFun(O.name) ) F  
    where F.schema_id= O.schema_id
    

    这是合法的。

    编辑:或者,更短的语法:(通过ErikE)

    select F.* from sys.objects O  
    cross apply dbo.myTableFun(O.name) F
    where F.schema_id= O.schema_id
    

    编辑:

    注意:Informix 12.10 xC2 +具有横向派生表,Postgresql(9.3+)具有可用于类似效果的横向子查询。


    考虑你有两张桌子。

    主表

    x------x--------------------x
    | Id   |        Name        |
    x------x--------------------x
    |  1   |          A         |
    |  2   |          B         |
    |  3   |          C         |
    x------x--------------------x
    

    细节表

    x------x--------------------x-------x
    | Id   |      PERIOD        |   QTY |
    x------x--------------------x-------x
    |  1   |   2014-01-13       |   10  |
    |  1   |   2014-01-11       |   15  |
    |  1   |   2014-01-12       |   20  |
    |  2   |   2014-01-06       |   30  |
    |  2   |   2014-01-08       |   40  |
    x------x--------------------x-------x
    

    有很多情况下我们需要用CROSS APPLY替换INNER JOIN

    1.根据TOP n结果加入两个表格

    考虑我们是否需要从Master Details table选择IdName ,并从Details table为每个Id选择最后两个日期。

    SELECT M.ID,M.NAME,D.PERIOD,D.QTY
    FROM MASTER M
    INNER JOIN
    (
        SELECT TOP 2 ID, PERIOD,QTY 
        FROM DETAILS D      
        ORDER BY CAST(PERIOD AS DATE)DESC
    )D
    ON M.ID=D.ID
    
  • SQL FIDDLE
  • 以上查询生成以下结果。

    x------x---------x--------------x-------x
    |  Id  |   Name  |   PERIOD     |  QTY  |
    x------x---------x--------------x-------x
    |   1  |   A     | 2014-01-13   |  10   |
    |   1  |   A     | 2014-01-12   |  20   |
    x------x---------x--------------x-------x
    

    请参阅它使用最后两个日期的Id生成最后两个日期的结果,然后仅在Id的外部查询中加入这些记录,这是错误的。 为了完成这个,我们需要使用CROSS APPLY

    SELECT M.ID,M.NAME,D.PERIOD,D.QTY
    FROM MASTER M
    CROSS APPLY
    (
        SELECT TOP 2 ID, PERIOD,QTY 
        FROM DETAILS D  
        WHERE M.ID=D.ID
        ORDER BY CAST(PERIOD AS DATE)DESC
    )D
    
  • SQL FIDDLE
  • 并形成以下结果。

    x------x---------x--------------x-------x
    |  Id  |   Name  |   PERIOD     |  QTY  |
    x------x---------x--------------x-------x
    |   1  |   A     | 2014-01-13   |  10   |
    |   1  |   A     | 2014-01-12   |  20   |
    |   2  |   B     | 2014-01-08   |  40   |
    |   2  |   B     | 2014-01-06   |  30   |
    x------x---------x--------------x-------x
    

    这是它的工作原理。 CROSS APPLY内部的查询可以引用外部表, INNER JOIN不能这样做(它会引发编译错误)。 找到最后两个日期时,加入是在CROSS APPLY内完成的,即WHERE M.ID=D.ID

    2.当我们需要使用函数的INNER JOIN功能时。

    当我们需要从Master表和一个function获得结果时, CROSS APPLY可以用作INNER JOIN的替换。

    SELECT M.ID,M.NAME,C.PERIOD,C.QTY
    FROM MASTER M
    CROSS APPLY dbo.FnGetQty(M.ID) C
    

    这是功能

    CREATE FUNCTION FnGetQty 
    (   
        @Id INT 
    )
    RETURNS TABLE 
    AS
    RETURN 
    (
        SELECT ID,PERIOD,QTY 
        FROM DETAILS
        WHERE ID=@Id
    )
    
  • SQL FIDDLE
  • 这产生了以下结果

    x------x---------x--------------x-------x
    |  Id  |   Name  |   PERIOD     |  QTY  |
    x------x---------x--------------x-------x
    |   1  |   A     | 2014-01-13   |  10   |
    |   1  |   A     | 2014-01-11   |  15   |
    |   1  |   A     | 2014-01-12   |  20   |
    |   2  |   B     | 2014-01-06   |  30   |
    |   2  |   B     | 2014-01-08   |  40   |
    x------x---------x--------------x-------x
    

    交叉应用的附加优点

    APPLY可以用作UNPIVOT的替代品。 CROSS APPLYOUTER APPLY可以在这里使用,它们可以互换。

    考虑你有下面的表(名为MYTABLE )。

    x------x-------------x--------------x
    |  Id  |   FROMDATE  |   TODATE     |
    x------x-------------x--------------x
    |   1  |  2014-01-11 | 2014-01-13   | 
    |   1  |  2014-02-23 | 2014-02-27   | 
    |   2  |  2014-05-06 | 2014-05-30   | 
    |   3  |     NULL    |    NULL      |
    x------x-------------x--------------x
    

    查询如下。

    SELECT DISTINCT ID,DATES
    FROM MYTABLE 
    CROSS APPLY(VALUES (FROMDATE),(TODATE))
    COLUMNNAMES(DATES)
    
  • SQL FIDDLE
  • 这给你带来了结果

      x------x-------------x
      | Id   |    DATES    |
      x------x-------------x
      |  1   |  2014-01-11 |
      |  1   |  2014-01-13 |
      |  1   |  2014-02-23 |
      |  1   |  2014-02-27 |
      |  2   |  2014-05-06 |
      |  2   |  2014-05-30 | 
      |  3   |    NULL     | 
      x------x-------------x
    
    链接地址: http://www.djcxy.com/p/2469.html

    上一篇: When should I use Cross Apply over Inner Join?

    下一篇: How to automatically generate a stacktrace when my gcc C++ program crashes