2009-07-16 63 views
722

使用CROSS APPLY的主要目的是什麼?什麼時候應該使用交叉套用內連接?

我已閱讀(隱約地通過互聯網上的帖子),如果您正在進行分區,在選擇大型數據集時cross apply可以更高效。 (尋呼想到)

我也知道,CROSS APPLYdoesn't require a UDF as the right-table.

在大多數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 
+41

我知道這是我甚至PICKIER但'高性能'絕對是一個字。這與效率無關。 – Rire1979 2010-12-07 19:07:02

+2

這對於sql xquery非常有用。檢查[this](http://stackoverflow.com/a/10511719/474679)。 – ARZ 2012-05-10 05:21:58

+2

似乎使用「內部循環連接」將非常接近交叉應用。我希望你的例子詳細說明哪個連接提示是等價的。只是說連接可能會導致內部/循環/合併甚至「其他」,因爲它可能會與其他連接重新排列。 – crokusek 2012-06-09 05:58:22

回答

535

誰能給我當CROSS APPLY使得在這些情況下INNER JOIN也能發揮作用的差異的一個很好的例子?

見我的博客文章進行詳細的性能對比:

CROSS APPLY工作更好的東西,有沒有簡單JOIN條件。

這一次選擇從t2每個記錄從t13最後一個記錄:

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記錄與PRIMARY KEYid的表。

這個查詢:

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 

是即時的。

5

我想這應該是可讀性;)

CROSS APPLY將是很獨特的人閱讀,告訴他們正在使用UDF將其從表中左側被應用到每一行。

當然,還有其他限制,其中CROSS APPLY比其他朋友在上面發佈的JOIN更適合使用。

170

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這可以用於類似的效果。

2

嗯,我不確定這是否符合使用交叉應用與內部聯接的理由,但此查詢在論壇帖子中使用交叉應用回答,所以我不確定是否存在使用均衡方法INNER JOIN:

Create PROCEDURE [dbo].[Message_FindHighestMatches] 

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

AS BEGIN

-- 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 

END

30

在我看來是CROSS APPLY可以填補一定的差距時worki ng與複雜/嵌套查詢中的計算字段進行比較,並使其更簡單,更具可讀性。

簡單示例:您有一個DoB,並且您想要呈現多個與年齡相關的字段,這些字段也將依賴其他數據源(例如就業),如Age,AgeGroup,AgeAtHiring,MinimumRetirementDate等在您的最終用戶應用程序(例如Excel數據透視表)。

選擇是有限的,很少優雅:

  • JOIN子查詢不能在基於父查詢數據數據集引入新的值(它必須站在自己)。

  • UDFs整齊,但速度慢,因爲它們傾向於阻止並行操作。作爲一個獨立的實體可以是一個好(少代碼)或壞(代碼在哪裏)的東西。

  • 連接表。有時他們可以工作,但很快你就會加入大量UNION的子查詢。大混亂。

  • 創建又一個單一目的視圖,假設您的計算不需要通過主要查詢中途獲得的數據。

  • 中介表。是的......通常起作用,並且通常是一個很好的選擇,因爲它們可以進行索引並且速度很快,但是由於UPDATE語句不是平行的,並且不允許級聯公式(重用結果)來更新同樣的聲明。有時候,你只是喜歡一次完成任務。

  • 嵌套查詢。是的,在任何時候,您都可以在整個查詢中放置括號,並將其用作可以操作源數據和計算字段的子查詢。但只有在它變得醜陋之前,你才能做到這一點。十分難看。

  • 重複代碼。 3長(CASE ... ELSE ... END)陳述的最大價值是多少?這將是可讀的!

    • 告訴你的客戶自己計算該死的東西。

我錯過了什麼嗎?可能吧,隨時發表評論。但是,嘿,CROSS APPLY在這種情況下就像是天賜之物:你只需添加一個簡單的CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl即可!您的新領域現在已經可以使用了,就好像它始終存在於您的源數據中一樣。

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

  • 使用而不會增加性能,複雜性和可讀性問題混合
  • 像連接,隨後的幾次CROSS APPLY語句可以把自己創建一個或多個計算領域:CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
  • 可以使用通過交叉引入值在隨後的JOIN條件
  • 作爲獎勵APPLY,還有的表值功能方面

蕩,有諾斯他們做不到!

12

交叉應用也適用於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 
2

跨應用可以將資料表被用來代替CE子查詢的,你需要的子查詢

子查詢

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 
95

考慮你有兩張桌子。

主表

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和最後兩個日期IdName每個IdDetails table

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 

上面的查詢產生以下結果。

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 

並形成以下結果。

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功能。

CROSS APPLY可當我們需要從Master表和function導致被用作與INNER JOIN的替代品。

在此可以產生以下結果的函數

CREATE FUNCTION FnGetQty 
( 
    @Id INT 
) 
RETURNS TABLE 
AS 
RETURN 
(
    SELECT ID,PERIOD,QTY 
    FROM DETAILS 
    WHERE [email protected] 
) 

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) 

,爲您帶來的結果

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 
0

這也許是一個老問題,但我還是喜歡CROSS的電源接通,簡化重 - 使用邏輯併爲結果提供「鏈接」機制。

我在下面提供了一個SQL小提琴,演示瞭如何使用CROSS APPLY在數據集上執行復雜的邏輯操作,而不會讓事情變得麻煩。從這裏推斷更復雜的計算並不難。

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

3

下面是解釋這一切,他們的表現差異,並使用了加入一個文章。

SQL Server CROSS APPLY and OUTER APPLY over JOINS

正如本文中建議,對於普通的聯接操作(內部和CROSS)它們之間沒有性能差異。

enter image description here

的使用差異到達時,你必須做一個查詢這樣的:

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完成,因爲INNER JOIN會給出錯誤「多部分標識符」D.DepartmentID「無法綁定。」在讀取每行時,將值傳遞給函數。聽起來很酷。 :)

相關問題