web-dev-qa-db-ja.com

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

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

私は(漠然と、インターネット上の投稿を通して)、パーティション分割をしている場合、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 がもっと速い/もっと効率的なところを見せてください)

__コードスニペット__
842

内部結合が同様に機能する場合に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とウィンドウ関数を使って、次のようなことができるでしょう。

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は、id20,000,000がある、およそ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

インスタントです。

617
Quassnoi

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 x C2 +には Lateral Derived Tables およびPostgresql(9.3+)には Lateral Subqueries があり、これを使用して同様の効果を得ることができます。

186
nurettin

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 JOINCROSS APPLYに置き換える必要がある状況はたくさんあります。

1. TOP nの結果に基づいて2つのテーブルを結合します

IdからNameMasterを選択し、Details tableの各Idに対して最後の2つの日付を選択する必要があるかどうかを検討してください。

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つの日付のIdを使って最後の2つの日付の結果を生成してから、これらのレコードを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はこれを行うことができません(コンパイルエラーが発生します)。最後の2つの日付を見つけるとき、結合はCROSS APPLYの内側、すなわちWHERE M.ID=D.IDの中で行われます。

2.関数を使ってINNER JOIN機能が必要なとき。

Masterテーブルとfunctionから結果を取得する必要がある場合、CROSS APPLYINNER 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 [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 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
143
Sarath Avanavu

私にとっては、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
  • 以降のJOIN条件では、CROSS APPLYによって導入された値を使用できます。
  • ボーナスとして、テーブル値関数の側面があります

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

37
mtone

これは、CROSS APPLYがパフォーマンスに大きな影響を与える場合の例です。

BETWEEN条件で結合を最適化するためにCROSS APPLYを使用する

内部結合を置き換える以外にも、スカラーUDFを含めることでパフォーマンスを犠牲にすることなく、日付を切り捨てるなどのコードを再利用することができます。たとえば、 インラインUDFを使用した月の3番目の水曜日 の計算)

36
A-K

クロスアプライは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
14
Chris

クロスアプライは、副照会の列が必要な場合に副照会を置き換えるために使用できます。

副照会

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
6

読みやすくするべきだと思います。

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

もちろん、他の友人が上に投稿したJOINよりもCROSS APPLYの方が使いやすいという他の制限があります。

5
shahkalpesh

これはすでに技術的に非常によく回答されていますが、それがいかに非常に有用であるかの具体的な例を挙げましょう。

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

私は顧客についての詳細と、彼らが行った最新の注文を私に与えるビューを作成したいです。 JOINSだけでは、これにはいくつかの自己結合と集約が必要になりますが、これは魅力的ではありません。しかし、Cross Applyを使えば、とても簡単です。

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

これは、JOINSとのパフォーマンスの違いと使用法について、すべて説明した記事です。

SQL ServerはJOINSを介してAPPL APPLYおよびOUTER APPLYを交差させます

この記事で示唆されているように、通常の結合操作(INNER AND 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を使用してこれを行うことはできません。その場合、エラー"マルチパート識別子" D.DepartmentID "をバインドできませんでした。"ここで値は各行として関数に渡されます。私にはクールに聞こえます。

4
Shanid

これがCross ApplyとInner Joinを使用する理由として適切かどうかはわかりませんが、Cross Applyを使用したフォーラム投稿でこのクエリに回答したので、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

終わり

3
user1054326

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

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

APPLY演算子での相関について言えば、右側に置くことができます。

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

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

1
Raf

これはおそらく古い質問ですが、論理の再利用を単純化し、結果に「連鎖」メカニズムを提供するというCROSS APPLYの力が今でも大好きです。

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

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

1
mrmillsy