CROSS APPLY を使用する主な目的は何ですか?
私は(漠然と、インターネット上の投稿を通して)、パーティション分割をしている場合、cross apply
が大きなデータセットを選択するときにもっと効率的になることができると読んでいます。 (ページングが頭に浮かぶ)
私はCROSS APPLY
が正しいテーブルとしてUDFを必要としないことも知っています。
ほとんどのINNER JOIN
クエリ(一対多の関係)では、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
がもっと速い/もっと効率的なところを見せてください)
__コードスニペット__
内部結合が同様に機能する場合に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
は、id
に20,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
インスタントです。
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 があり、これを使用して同様の効果を得ることができます。
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
を選択し、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 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
)
次のような結果になった
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
私にとっては、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を通じて導入された値は次のことが可能です。
CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
Dang、彼らができないことは何もない!
これは、CROSS APPLYがパフォーマンスに大きな影響を与える場合の例です。
BETWEEN条件で結合を最適化するためにCROSS APPLYを使用する
内部結合を置き換える以外にも、スカラーUDFを含めることでパフォーマンスを犠牲にすることなく、日付を切り捨てるなどのコードを再利用することができます。たとえば、 インラインUDFを使用した月の3番目の水曜日 の計算)
クロスアプライは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
クロスアプライは、副照会の列が必要な場合に副照会を置き換えるために使用できます。
副照会
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
読みやすくするべきだと思います。
CROSS APPLYは、左側の表の各行に適用されるUDFが使用されていることを伝えるために読んでいる人にとってはややユニークです。
もちろん、他の友人が上に投稿したJOINよりもCROSS APPLYの方が使いやすいという他の制限があります。
これはすでに技術的に非常によく回答されていますが、それがいかに非常に有用であるかの具体的な例を挙げましょう。
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
これは、JOINSとのパフォーマンスの違いと使用法について、すべて説明した記事です。
SQL ServerはJOINSを介してAPPL APPLYおよびOUTER APPLYを交差させます
この記事で示唆されているように、通常の結合操作(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)
それはあなたが機能と関係しなければならないときです。 INNER JOINを使用してこれを行うことはできません。その場合、エラー"マルチパート識別子" D.DepartmentID "をバインドできませんでした。"ここで値は各行として関数に渡されます。私にはクールに聞こえます。
これが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
終わり
APPLY演算子の本質は、FROM句で演算子の左側と右側の間の相関を可能にすることです。
結合とは対照的に、入力間の相関は許可されません。
APPLY演算子での相関について言えば、右側に置くことができます。
どちらも複数の列と行を返すことができます。
これはおそらく古い質問ですが、論理の再利用を単純化し、結果に「連鎖」メカニズムを提供するというCROSS APPLYの力が今でも大好きです。
以下にSQL Fiddleを用意しました。これは、何も面倒くさいことなく、CROSS APPLYを使用してデータセットに対して複雑な論理演算を実行する方法の簡単な例を示しています。ここから、より複雑な計算を推定するのは難しくありません。