我啥时候应该使用 CROSS APPLY 而不是 INNER JOIN?

Posted

技术标签:

【中文标题】我啥时候应该使用 CROSS APPLY 而不是 INNER JOIN?【英文标题】:When should I use CROSS APPLY over INNER JOIN?我什么时候应该使用 CROSS APPLY 而不是 INNER JOIN? 【发布时间】:2009-07-16 17:42:21 【问题描述】:

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

我已经读到(模糊地,通过 Internet 上的帖子)cross apply 在选择大型数据集时可以更有效,如果您正在分区。 (想到分页)

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

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

谁能给我一个很好的例子,说明CROSS APPLYINNER 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

【问题讨论】:

我知道这对我来说更挑剔,但“性能”绝对是一个词。它只是与效率无关。 对sql xquery非常有用。检查this。 似乎使用“内循环连接”将非常接近交叉应用。我希望您的示例详细说明哪个连接提示是等效的。只是说加入可能会导致内部/循环/合并甚至“其他”,因为它可能会与其他联接重新排列。 当连接将创建很多行但您一次只需要评估一个行连接时。我有一个案例,我需要在一个超过 1 亿行的表上进行自联接,并且内存不足。所以我用光标来降低内存占用。从光标开始,我交叉应用为仍然托管的内存占用,并且比光标快 1/3。 CROSS APPLY 在允许一个集合依赖另一个集合方面具有明显的用途(与 JOIN 运算符不同),但这并非没有代价:它的行为就像一个在left 集合的每个成员,因此,在 SQL Server 术语中,它总是执行Loop Join,这几乎不是连接集合的最佳方式。所以,在需要的时候使用APPLY,但不要过度使用JOIN 【参考方案1】:

谁能给我一个很好的例子,说明何时 CROSS APPLY 在 INNER JOIN 也可以工作的情况下有所作为?

查看我博客中的文章了解详细的性能比较:

INNER JOIN vs. CROSS APPLY

CROSS APPLY 在没有简单的JOIN 条件的情况下效果更好。

这个从t2 中选择3 的最后一条记录,用于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 是一个大约有 20,000,000 记录的表,在 id 上带有 PRIMARY KEY

这个查询:

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

是即时的。

【讨论】:

查看 Ariel 链接的末尾。 row_number() 查询同样好,甚至不需要连接。所以我认为我不应该在这种情况下使用交叉申请(选择前 3 名,按 t1.id 分区)。 虽然这是最受欢迎的答案,但我认为它不能回答实际问题“使用 CROSS APPLY 的主要目的是什么?”。主要目的是让带参数的表函数每行执行一次,然后加入结果。 @Mike: 你怎么称呼TVFINNER JOIN @MikeKulls 是的,但是 OP 没有要求使用 CROSS APPLY 的主要目的,他询问何时选择它而不是 INNER JOIN,什么时候也可以。跨度> 值得一提的是,这在标准 (ANSI) SQL 中称为 lateral join【参考方案2】:

假设您有两张桌子。

主表

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

有很多情况我们需要将INNER JOIN替换为CROSS APPLY

1.根据TOP n 结果连接两个表

考虑我们是否需要从Master 中选择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 的外部查询中加入这些记录,这是错误的。这应该返回 Ids 1 和 2 但它只返回 1 因为 1 有最后两个日期。为此,我们需要使用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

【讨论】:

2 vs 4 记录的优秀示例,帮助我了解需要这样做的上下文。 对于第 1 点,我们有 2 行用于 ID 1,而不是 4 行用于 ID 1、2。我们不应该只使用左连接吗?【参考方案3】:

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+ 有Lateral Derived Tables,Postgresql (9.3+) 有Lateral Subqueries,可以使用类似的效果。

【讨论】:

我认为这就是我们交叉申请的原因。如果您查看下面的链接,这是 MS 所说的关于交叉应用的第一件事。它可能还有其他用途,但我认为这就是引入它的原因。没有它,表函数在很多情况下都无法使用。 technet.microsoft.com/en-us/library/ms175156.aspx cross apply 在与内联表函数结合使用时也能产生一个很好的执行计划,同时保持急需的模块化。 CROSS APPLY 中不需要SELECT。请尝试CROSS APPLY dbo.myTableFun(O.name) F @ErikE 当然,您始终可以使用不太灵活的语法来交叉应用。我展示了更通用的版本,您有时可以使用它以避免将难以计算的列带入查询。 @Bolu 内连接如果表函数参数依赖于外部选择中另一个表的列(也称为外部引用),则将不起作用。如果表函数参数是文字或变量,它将起作用。交叉应用适用于这两种情况。【参考方案4】:

在我看来,CROSS APPLY 在处理复杂/嵌套查询中的计算字段时可以填补一定的空白,并使它们更简单、更具可读性。

简单示例:您有一个 DoB,并且您希望呈现多个与年龄相关的字段,这些字段也将依赖于其他数据源(例如就业),例如 Age、AgeGroup、AgeAtHiring、MinimumRetirementDate 等以供您最终使用- 用户应用程序(例如 Excel 数据透视表)。

选项有限且很少优雅:

JOIN 子查询不能基于父查询中的数据在数据集中引入新值(它必须独立存在)。

UDF 很简洁,但速度很慢,因为它们往往会阻止并行操作。作为一个独立的实体可能是一件好事(更少的代码)或一件坏事(代码在哪里)。

连接表。有时它们可​​以工作,但很快你就会加入带有大量 UNION 的子查询。大混乱。

假设您的计算不需要在主查询中途获得的数据,则创建另一个单一用途视图。

中间表。是的...这通常有效,并且通常是一个不错的选择,因为它们可以被索引并且速度很快,但是由于 UPDATE 语句不是并行的并且不允许级联公式(重用结果)来更新多个字段,性能也会下降同样的声明。有时你更喜欢一次性完成。

嵌套查询。是的,您可以在任何时候在整个查询上加上括号并将其用作子查询,您可以在其上操作源数据和计算字段等。但是你只能在它变得丑陋之前做到这一点。很丑。

重复代码。 3 个长 (CASE...ELSE...END) 语句的最大值是多少?这将是可读的!

告诉您的客户自己计算该死的东西。

我错过了什么吗?可能,所以请随时发表评论。但是,嘿,CROSS APPLY 在这种情况下就像天赐之物:您只需添加一个简单的CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl 就可以了!您的新字段现在几乎可以使用,就像它一直存在于您的源数据中一样。

通过 CROSS APPLY 引入的值可以...

用于创建一个或多个计算字段,而不会增加性能、复杂性或可读性问题 和 JOIN 一样,后面的几个 CROSS APPLY 语句可以引用自己:CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2 您可以在后续的 JOIN 条件中使用 CROSS APPLY 引入的值 另外,还有表值函数方面

该死,没有什么是他们做不到的!

【讨论】:

这是我的一个很大的 +1,因为我很惊讶它没有被更多地提及。也许您可以扩展此示例以显示如何在派生值链上执行“程序”计算?例如:CROSS APPLY(选择 crossTbl.value * tbl.multiplier 作为 Multiplied)multiTbl - CROSS APPLY(选择 multiTbl.Multiplied / tbl.DerivativeRatio 作为 Derived)derivedTbl - 等等 ... 关于如何使用 Cross Apply 代替 CASE..ELSE..END 的更多信息/示例? @przemo_li APPLY 可用于存储 case 语句的结果(以及其他内容)以便引用它。结构可能类似于:SELECT CASE when subquery.intermediateResult > 0 THEN "yes" ELSE "no" END FROM someTable OUTER APPLY (select CASE...END...ELSE as intermediateResult) 作为子查询。 交叉应用计算示例col.sql.drylib.com【参考方案5】:

这在技术上已经得到了很好的回答,但让我举一个具体的例子来说明它是如何非常有用的:

假设您有两个表,客户和订单。客户有很多订单。

我想创建一个视图,为我提供有关客户的详细信息以及他们最近下的订单。仅使用 JOINS,这将需要一些不漂亮的自连接和聚合。但是使用 Cross Apply,它超级简单:

SELECT *
FROM Customer
CROSS APPLY (
  SELECT TOP 1 *
  FROM Order
  WHERE Order.CustomerId = Customer.CustomerId
  ORDER BY OrderDate DESC
) T

【讨论】:

【参考方案6】:

交叉应用也适用于 XML 字段。如果您希望结合其他字段选择节点值。

例如,如果您有一个包含一些 xml 的表

<root>
    <subnode1>
       <some_node value="1" />
       <some_node value="2" />
       <some_node value="3" />
       <some_node value="4" />
    </subnode1>
</root>

使用查询

SELECT
       id as [xt_id]
      ,xmlfield.value('(/root/@attribute)[1]', 'varchar(50)') root_attribute_value
  ,node_attribute_value = [some_node].value('@value', 'int')
  ,lt.lt_name   
FROM dbo.table_with_xml xt
CROSS APPLY xmlfield.nodes('/root/subnode1/some_node') as g ([some_node])
LEFT OUTER JOIN dbo.lookup_table lt
ON [some_node].value('@value', 'int') = lt.lt_id

会返回结果

xt_id root_attribute_value node_attribute_value lt_name
----------------------------------------------------------------------
1     test1            1                    Benefits
1     test1            4                    FINRPTCOMPANY

【讨论】:

【参考方案7】:

交叉应用可用于替换需要子查询列的子查询

子查询

select * from person p where
p.companyId in(select c.companyId from company c where c.companyname like '%yyy%')

在这里我将无法选择公司表的列 所以,使用交叉应用

select P.*,T.CompanyName
from Person p
cross apply (
    select *
    from Company C
    where p.companyid = c.companyId and c.CompanyName like '%yyy%'
) T

【讨论】:

【参考方案8】:

这是一个简短的教程,可以保存在 .sql 文件中并在 SSMS 中执行,我为自己编写了该教程,以快速刷新我对 CROSS APPLY 的工作原理以及何时使用它的记忆:

-- Here's the key to understanding CROSS APPLY: despite the totally different name, think of it as being like an advanced 'basic join'.
-- A 'basic join' gives the Cartesian product of the rows in the tables on both sides of the join: all rows on the left joined with all rows on the right.
-- The formal name of this join in SQL is a CROSS JOIN.  You now start to understand why they named the operator CROSS APPLY.

-- Given the following (very) simple tables and data:
CREATE TABLE #TempStrings ([SomeString] [nvarchar](10) NOT NULL);
CREATE TABLE #TempNumbers ([SomeNumber] [int] NOT NULL);
CREATE TABLE #TempNumbers2 ([SomeNumber] [int] NOT NULL);
INSERT INTO #TempStrings VALUES ('111'); INSERT INTO #TempStrings VALUES ('222');
INSERT INTO #TempNumbers VALUES (111); INSERT INTO #TempNumbers VALUES (222);
INSERT INTO #TempNumbers2 VALUES (111); INSERT INTO #TempNumbers2 VALUES (222); INSERT INTO #TempNumbers2 VALUES (222);

-- Basic join is like CROSS APPLY; 2 rows on each side gives us an output of 4 rows, but 2 rows on the left and 0 on the right gives us an output of 0 rows:
SELECT
    st.SomeString, nbr.SomeNumber
FROM -- Basic join ('CROSS JOIN')
    #TempStrings st, #TempNumbers nbr
    -- Note: this also works:
    --#TempStrings st CROSS JOIN #TempNumbers nbr

-- Basic join can be used to achieve the functionality of INNER JOIN by first generating all row combinations and then whittling them down with a WHERE clause:
SELECT
    st.SomeString, nbr.SomeNumber
FROM -- Basic join ('CROSS JOIN')
    #TempStrings st, #TempNumbers nbr
WHERE
    st.SomeString = nbr.SomeNumber

-- However, for increased readability, the SQL standard introduced the INNER JOIN ... ON syntax for increased clarity; it brings the columns that two tables are
-- being joined on next to the JOIN clause, rather than having them later on in the WHERE clause.  When multiple tables are being joined together, this makes it
-- much easier to read which columns are being joined on which tables; but make no mistake, the following syntax is *semantically identical* to the above syntax:
SELECT
    st.SomeString, nbr.SomeNumber
FROM -- Inner join
    #TempStrings st INNER JOIN #TempNumbers nbr ON st.SomeString = nbr.SomeNumber

-- Because CROSS APPLY is generally used with a subquery, the subquery's WHERE clause will appear next to the join clause (CROSS APPLY), much like the aforementioned
-- 'ON' keyword appears next to the INNER JOIN clause.  In this sense, then, CROSS APPLY combined with a subquery that has a WHERE clause is like an INNER JOIN with
-- an ON keyword, but more powerful because it can be used with subqueries (or table-valued functions, where said WHERE clause can be hidden inside the function).
SELECT
    st.SomeString, nbr.SomeNumber
FROM
    #TempStrings st CROSS APPLY (SELECT * FROM #TempNumbers tempNbr WHERE st.SomeString = tempNbr.SomeNumber) nbr

-- CROSS APPLY joins in the same way as a CROSS JOIN, but what is joined can be a subquery or table-valued function.  You'll still get 0 rows of output if
-- there are 0 rows on either side, and in this sense it's like an INNER JOIN:
SELECT
    st.SomeString, nbr.SomeNumber
FROM
    #TempStrings st CROSS APPLY (SELECT * FROM #TempNumbers tempNbr WHERE 1 = 2) nbr

-- OUTER APPLY is like CROSS APPLY, except that if one side of the join has 0 rows, you'll get the values of the side that has rows, with NULL values for
-- the other side's columns.  In this sense it's like a FULL OUTER JOIN:
SELECT
    st.SomeString, nbr.SomeNumber
FROM
    #TempStrings st OUTER APPLY (SELECT * FROM #TempNumbers tempNbr WHERE 1 = 2) nbr

-- One thing CROSS APPLY makes it easy to do is to use a subquery where you would usually have to use GROUP BY with aggregate functions in the SELECT list.
-- In the following example, we can get an aggregate of string values from a second table based on matching one of its columns with a value from the first
-- table - something that would have had to be done in the ON clause of the LEFT JOIN - but because we're now using a subquery thanks to CROSS APPLY, we
-- don't need to worry about GROUP BY in the main query and so we don't have to put all the SELECT values inside an aggregate function like MIN().
SELECT
    st.SomeString, nbr.SomeNumbers
FROM
    #TempStrings st CROSS APPLY (SELECT SomeNumbers = STRING_AGG(tempNbr.SomeNumber, ', ') FROM #TempNumbers2 tempNbr WHERE st.SomeString = tempNbr.SomeNumber) nbr
-- ^ First the subquery is whittled down with the WHERE clause, then the aggregate function is applied with no GROUP BY clause; this means all rows are
--   grouped into one, and the aggregate function aggregates them all, in this case building a comma-delimited string containing their values.

DROP TABLE #TempStrings;
DROP TABLE #TempNumbers;
DROP TABLE #TempNumbers2;

【讨论】:

非常感谢!【参考方案9】:

我想应该是可读性;)

CROSS APPLY 对于阅读的人来说有些独特,告诉他们正在使用 UDF,该 UDF 将应用于左侧表格中的每一行。

当然,CROSS APPLY 比上面其他朋友发布的 JOIN 更好地使用还有其他限制。

【讨论】:

【参考方案10】:

APPLY 运算符的本质是在 FROM 子句中允许运算符的左右两边关联。

相对于JOIN,输入之间的关联是不允许的。

谈到 APPLY 运算符中的相关性,我的意思是我们可以在右侧放置:

派生表 - 作为具有别名的相关子查询 表值函数 - 带参数的概念视图,其中参数可以引用左侧

两者都可以返回多列和多行。

【讨论】:

【参考方案11】:

这是一篇解释这一切的文章,以及它们在 JOINS 上的性能差异和用法。

SQL Server CROSS APPLY and OUTER APPLY over JOINS

正如本文所建议的,对于普通的连接操作(INNER 和 CROSS),它们之间没有性能差异。

当您必须进行这样的查询时,使用差异就会出现:

CREATE FUNCTION dbo.fn_GetAllEmployeeOfADepartment(@DeptID AS INT)  
RETURNS TABLE 
AS 
RETURN 
   ( 
   SELECT * FROM Employee E 
   WHERE E.DepartmentID = @DeptID 
   ) 
GO 

SELECT * FROM Department D 
CROSS APPLY dbo.fn_GetAllEmployeeOfADepartment(D.DepartmentID)

也就是说,当您必须与功能相关时。这不能使用 INNER JOIN 完成,这会给您错误“无法绑定多部分标识符“D.DepartmentID”。” 这里传递了值在读取每一行时传递给函数。对我来说听起来很酷。 :)

【讨论】:

【参考方案12】:

好吧,我不确定这是否有资格作为使用 Cross Apply 与 Inner Join 的理由,但是在论坛帖子中使用 Cross Apply 为我回答了这个问题,所以我不确定是否有使用 Inner 的等价方法加入:

Create PROCEDURE [dbo].[Message_FindHighestMatches]

-- Declare the Topical Neighborhood
@TopicalNeighborhood nchar(255)

作为 开始

-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON

Create table  #temp
(
    MessageID         int,
    Subjects          nchar(255),
    SubjectsCount    int
)

Insert into #temp Select MessageID, Subjects, SubjectsCount From Message

Select Top 20 MessageID, Subjects, SubjectsCount,
    (t.cnt * 100)/t3.inputvalues as MatchPercentage

From #temp 

cross apply (select count(*) as cnt from dbo.Split(Subjects,',') as t1
             join dbo.Split(@TopicalNeighborhood,',') as t2
             on t1.value = t2.value) as t
cross apply (select count(*) as inputValues from dbo.Split(@TopicalNeighborhood,',')) as t3

Order By MatchPercentage desc

drop table #temp

结束

【讨论】:

【参考方案13】:

这可能是一个老问题,但我仍然喜欢 CROSS APPLY 的强大功能,它可以简化逻辑的重用并为结果提供“链接”机制。

我在下面提供了一个 SQL Fiddle,它显示了一个简单的示例,说明如何使用 CROSS APPLY 对数据集执行复杂的逻辑操作,而不会让事情变得一团糟。从这里不难推断出更复杂的计算。

http://sqlfiddle.com/#!3/23862/2

【讨论】:

【参考方案14】:

虽然大多数使用 CROSS APPLY 的查询可以使用 INNER JOIN 重写,但 CROSS APPLY 可以产生更好的执行计划和更好的性能,因为它可以在连接发生之前限制正在连接的集合。

盗自Here

【讨论】:

一年后,我回来阅读同样的问题,并找到了我自己的答案,坦率地说我不记得写了!【参考方案15】:

我们使用 CROSS APPLY 从另一个(更新请求)表中使用 JSON 更新一个表 - 因为我们使用 OPENJSON 来读取JSON 的内容,OPENJSON 是一个“表值函数”。

我打算在这里放一个我们的 UPDATE 命令的简化版本作为示例,但即使是简化的,它也相当大且过于复杂。因此,仅对部分命令进行如此简化的“草图”就足够了:

SELECT  
       r.UserRequestId,
       j.xxxx AS xxxx,
FROM  RequestTable as r WITH (NOLOCK)
   CROSS APPLY
      OPENJSON(r.JSON, '$.requesttype.recordtype')
      WITH(
             r.userrequestid nvarchar(50) '$.userrequestid',
             j.xxx nvarchar(20) '$.xxx
           )j
       WHERE r.Id > @MaxRequestId
          and ... etc. ....

【讨论】:

以上是关于我啥时候应该使用 CROSS APPLY 而不是 INNER JOIN?的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候应该使用 mongoDB 而不是关系数据库

我啥时候应该在子进程中使用`wait`而不是`communicate`?

我啥时候应该继承 EnumMeta 而不是 Enum?

我啥时候应该使用 StringComparison.InvariantCulture 而不是 StringComparison.CurrentCulture 来测试字符串是不是相等?

我啥时候应该选择 SAX 而不是 StAX?

我啥时候应该使用导航控制器?