内部結合でクロス適用を使用する必要があるのはいつですか?


925

CROSS APPLYを使用する主な目的は何ですか?

私は(漠然と、インターネット上の投稿を通じて)、cross applyパーティション分割している場合に大規模なデータセットを選択する場合により効率的な方法を読んだことがあります。(ページングが頭に浮かぶ)

またCROSS APPLY、右のテーブルとしてUDFを必要としないことも知っています。

ほとんどのINNER JOINクエリ(1対多の関係)では、を使用するように書き換えることができましたCROSS APPLYが、常に同等の実行プランが提供されます。

誰もが私がうまくいくような場合にCROSS APPLY違いが出るときの良い例を私に教えてもらえ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

50
私はこれが偶然のピキアーであることは知っていますが、「演奏者」は間違いなく言葉です。効率とは関係ありません。
Rire1979、2007

2
これは、sql xqueryに非常に役立ちます。これを確認してください。
ARZ

3
「内部ループ結合」の使用はクロス適用に非常に近いようです。私はあなたの例がどの結合ヒントが同等であることを詳述したいと思います。結合と言うだけでは、他の結合と並べ替えられる可能性があるため、内部/ループ/マージまたは「その他」になる可能性があります。
crokusek

3
結合によって多数の行が作成されるが、一度に1つの行結合のみを評価する必要がある場合。1億行を超えるテーブルで自己結合が必要で、メモリが不足している場合がありました。そこで、カーソルを移動してメモリのフットプリントを減らしました。カーソルから、まだ適用されているメモリのフットプリントとしてクロス適用を行い、カーソルよりも1/3高速でした。
パパラッツォ

10
CROSS APPLYJOIN演算子とは異なり)セットが別のセットに依存することを許可することには明らかな使用法がありますが、それにはコストがかかりません:左のセットの各メンバーを操作する関数のように動作するため、SQL Serverでは常にを実行しLoop Joinます。これは、ほとんどの場合、セットを結合する最良の方法ではありません。したがって、APPLY必要なときに使用しますが、に対して過度に使用しないでくださいJOIN
ヘラルドリマ2014

回答:


668

INNER JOINが機能する場合にCROSS 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とwindow関数を使ってそのようなことをすることができます:

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がonの約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

インスタントです。


2
アリエルのリンクの終わりを見てください。row_number()クエリも同様に優れており、結合も必要ありません。したがって、この状況ではクロスアプライを使用すべきではないと思います(上位3、t1.idによるパーティションを選択)。
ジェフミートボールヤン

375
これは最も一般的な回答ですが、「CROSS APPLYを使用する主な目的は何ですか?」という実際の質問には答えないと思います。主な目的は、パラメーターを持つテーブル関数を行ごとに1回実行して、結果に結合できるようにすることです。
MikeKulls 2011

5
@マイク:あなたはどうやってTVFwith を呼び出すのINNER JOINですか?
Quassnoi 2011

15
@MikeKullsはい、ただしOPはを使用する主な目的を要求しませんでしたCROSS APPLY。彼は、それを選択するINNER JOINタイミングと、それが機能する場合を尋ねました。
ErikE 2013

8
これはlateral join標準(ANSI)SQL と呼ばれていることに言及する価値があるかもしれません
a_horse_with_no_name

198

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+)にはラテラルサブクエリがあり、これを使用して同様の効果を得ることができます。


11
これがクロス申請の理由の背後にある理由だと思います。以下のリンクをチェックすると、これはMSがクロスアプライについて最初に言うことです。他の用途があるかもしれませんが、これが導入された理由だと思います。これがなければ、テーブル関数は多くの状況で使用できなくなります。 technet.microsoft.com/en-us/library/ms175156.aspx
MikeKulls '12 / 08/12

クロス適用は、必要なモジュール性を維持しながらインラインテーブル関数と組み合わせると、優れた実行プランも生成します。
nurettin

14
SELECT内部では必要ありませんCROSS APPLY。ぜひお試しくださいCROSS APPLY dbo.myTableFun(O.name) F
ErikE

1
@ErikE確かに、クロス適用するために常に柔軟性の低い構文を使用できます。クエリに列を計算するのが難しくなるのを避けるために時々使用できるより一般化されたバージョンを示していました。
nurettin 2013

2
@Bolu内部結合は、テーブル関数パラメーターが外部選択の別のテーブルの列(別名外部参照)に依存している場合は機能しません。テーブル関数パラメーターがリテラルまたは変数の場合に機能します。クロス適用はどちらの場合でも機能します。
ヌレティン2015年

175

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結果に基づいて2つのテーブルを結合する

私たちが選択する必要がある場合を検討IdしてNameから、Masterそれぞれの最後の2つの日付IdからDetails 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

ご覧のとおり、最後の2つの日付で最後の2つの日付の結果を生成Idし、これらのレコードをの外部クエリでのみ結合しましたがId、これは誤りです。これはIds1と2の両方を返すはずですが、1には最後の2つの日付があるため、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

次の結果を形成します。

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これはできません(コンパイルエラーがスローされます)。最後の2つの日付を見つける場合、結合はCROSS APPLYie 内で行われWHERE M.ID=D.IDます。

2. INNER JOIN関数を使用した機能が必要な場合。

CROSS APPLYtableとa INNER JOINから結果を取得する必要がある場合の代替として使用できます。Masterfunction

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
)

次の結果を生成しました

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 APPLYまたはOUTER 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

4
2対4のレコードの優れた例であり、これが必要になる状況を理解するのに役立ちました。
trnelson 2017

13
この回答は、承認されたページを選択するだけでなく、ページを下にスクロールすることが本当に価値があることを証明しています。
モスタファアルマンディ2018

2
これまでのAPPLYの使用法を説明するための最良の例...私は多くの投稿を読みましたが、この説明が水としての絵を明確にすることに気付きました。仲間に感謝します。
AG7 2018年

1
ID 1に4行ではなく、ID 1に2行あるポイント1の場合、代わりに左結合を使用しませんか?
Joseph Cho

43

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を通じて導入された値は次のことができます...

  • パフォーマンス、複雑さ、読みやすさの問題を追加することなく、1つまたは複数の計算フィールドを作成するために使用されます
  • JOINの場合と同様に、後続のいくつかのCROSS APPLYステートメントはそれ自体を参照できます。 CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
  • CROSS APPLYによって導入された値を後続のJOIN条件で使用できます
  • おまけとして、テーブル値関数の側面があります

おい、彼らができないことは何もない!


1
これはあまり言及されないことに驚いたので、これは私からの大きな+1です。おそらく、この例を拡張して、派生値のチェーンに対して「手続き型」計算を実行する方法を示すことができますか?例:CROSS multiTbl(逓倍ようtbl.multiplier * crossTbl.valueを選択)を適用- CROSS(派生としてmultiTbl.Multiplied / tbl.DerivativeRatioを選択)を適用derivedTbl -など...
mrmillsy

1
CASE..ELSE..ENDの代わりにCross Applyを使用する方法に関するその他の情報/例はありますか?
przemo_li 2016

3
@przemo_li APPLYは、(特に)ケースステートメントの結果を格納して、それを参照するために使用できます。構造は次のようなものになります。subquery.intermediateResult> 0の場合のSELECT CASE「はい」ELSE「いいえ」
mtone

14

クロス適用は、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

13

これはすでに技術的に非常によく答えられていますが、それがどのように非常に役立つかについて具体的な例を挙げましょう:

CustomerとOrderという2つのテーブルがあるとします。顧客には多くの注文があります。

顧客の詳細と、顧客が行った最新の注文を示すビューを作成したいと思います。JOINSだけの場合、これにはいくつかの自己結合と集計が必要になります。しかし、クロスアプライを使用すると、非常に簡単です。

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

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

5

私はそれが読みやすさであるべきだと思います;)

CROSS APPLYは、左側の表の各行に適用されるUDFが使用されていることを読むために読む人にとって、いくぶんユニークです。

もちろん、他の友達が上に投稿したJOINよりもCROSS APPLYを使うほうが他にも制限があります。


4

ここでは、JOINSと比較した場合のパフォーマンスの違いと使用法について、すべてを説明する記事を示します。

SQL Server CROSS APPLYおよびOUTER APPLYをJOINS経由で

この記事で提案されているように、通常の結合操作(INNER AND 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)

つまり、機能に関係する必要がある場合です。これは、「マルチパート識別子「D.DepartmentID」をバインドできませんでした」というエラーが発生するINNER JOINを使用して行うことはできません。ここでは、各行が読み取られるときに値が関数に渡されます。クールに聞こえます。:)


3

まあ、これがクロスアプライとインナージョインを使用する理由に該当するかどうかはわかりませんが、このクエリはクロスアプライを使用するフォーラム投稿で答えられたため、インナージョインを使用するequaliventメソッドがあるかどうかはわかりません。

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

終わり


3

APPLY演算子の本質は、FROM句で演算子の左側と右側の相関を可能にすることです。

JOINとは対照的に、入力間の相関は許可されません。

APPLY演算子の相関について言えば、私たちは右側に置くことができることを意味します:

  • 派生テーブル-エイリアスを持つ相関サブクエリとして
  • テーブル値関数-パラメータ付きの概念図。パラメータは左側を参照できます

どちらも複数の列と行を返すことができます。


2

これはおそらく古い質問ですが、ロジックの再利用を簡素化し、結果に「チェーン」メカニズムを提供するCROSS APPLYの力は今でも気に入っています。

以下のSQL Fiddleを提供します。これは、CROSS APPLYを使用して、煩雑になることなくデータセットに対して複雑な論理演算を実行する簡単な例を示しています。ここからより複雑な計算を推定することは難しくありません。

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


1

CROSS APPLYを使用するほとんどのクエリはINNER JOINを使用して書き換えることができますが、CROSS APPLYは、結合が発生する前に結合されるセットを制限できるため、実行プランとパフォーマンスが向上します。

ここから盗まれた

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.