Cross Apply Over Inner結合はいつ使用する必要がありますか?
-
16-09-2019 - |
質問
使用する主な目的は何ですか クロスを適用します?
私は、インターネット上の投稿を通して漠然と読みました) 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
解決
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
'sと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
のテーブルです 20,000,000
を含むレコード PRIMARY KEY
の上 id
.
このクエリ:
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 xc2+ has 横方向の派生テーブル およびpostgreSql(9.3+)があります 横方向のサブ征服 同様の効果に使用できます。
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.に基づいて2つのテーブルを結合します TOP n
結果
選択する必要があるかどうかを検討してください 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
, 、それは間違っています。これを達成するには、使用する必要があります 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
関数を使用した機能。
CROSS APPLY
代替として使用できます INNER JOIN
結果を得る必要があるとき Master
テーブルとa function
.
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
Crossの追加の利点が適用されます
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があり、年齢、年齢層、老化、最小retirementDateなど、他のデータソース(雇用など)にも依存する複数の年齢に関連したフィールドを提示する必要があります。エンドユーザーアプリケーションで使用する(たとえば、excel pivottables)。
オプションは限られており、めったにエレガントではありません:
SubQueriesに参加することは、親クエリのデータに基づいてデータセットに新しい値を導入できません(独自に立つ必要があります)。
UDFはきちんとしていますが、並列操作を防ぐ傾向があるため、遅いです。そして、別のエンティティであることは、良い(コードが少ない)または悪い(コードはどこですか)ことです。
ジャンクションテーブル。時々彼らは働くことができますが、すぐにあなたはたくさんの組合と一緒にサブ征服に参加しています。大きな混乱。
計算がメインクエリの途中で取得されたデータを必要としないと仮定すると、さらに別の単一目的ビューを作成します。
中間表。はい...それは通常機能し、多くの場合、インデックスと高速にすることができるため良いオプションですが、パフォーマンスは並列ではなく、カスケード式(結果を再利用する)を許可しないためにステートメントを更新するために低下する可能性があります。同じ声明。そして、時にはあなたはただ一つのパスで物事をすることを好むでしょう。
ネストクエリ。はい、いつでもクエリ全体に括弧を付けることができ、ソースデータと計算フィールドを操作できるサブクエリとして使用できます。しかし、あなたはそれが醜くなる前にしかこれを行うことができません。とても醜い。
繰り返しコード。 3ロング(ケース...他の...終了)ステートメントの最大の価値は何ですか?それは読みやすくなります!
- クライアントに自分でいまいましいことを計算するように伝えてください。
私は何か見落としてますか?おそらく、お気軽にコメントしてください。しかし、ちょっと、Cross Applyはそのような状況では天の恵みのようなものです:あなたは単純なものを追加するだけです CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl
そしてボイラ!新しいフィールドは、ソースデータに常にあったように、実際に使用できるようになりました。
Cross Applyを介して導入された値は...
- パフォーマンス、複雑さ、または読みやすさの問題をミックスに追加せずに、1つまたは複数の計算されたフィールドを作成するために使用されます
- 参加と同様に、その後のいくつかのCross Applyステートメントは自分自身を指すことができます。
CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
- 後続の結合条件でクロスによって導入された値を使用できます
- ボーナスとして、テーブル値の関数の側面があります
ダン、彼らができないことは何もありません!
Cross Applyは、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つのテーブルがあるとしましょう。顧客には多くの注文があります。
顧客についての詳細と、彼らが行った最新の注文を提供するビューを作成したいと思います。参加するだけで、これにはいくつかの自己参加と集約が必要になります。しかし、クロスが適用されると、その非常に簡単です:
SELECT *
FROM Customer
CROSS APPLY (
SELECT TOP 1 *
FROM Order
WHERE Order.CustomerId = Customer.CustomerId
ORDER BY OrderDate DESC
) T
Cross Applyを使用して、Subqueryの列が必要な場合にサブクエリを置き換えることができます
サブクエリ
select * from person p where
p.companyId in(select c.companyId from company c where c.companyname like '%yyy%')
ここでは、Cross Applyを使用して、会社テーブルの列を選択することはできません。
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が使用されていることを読んでいる人にとって、ややユニークです。
もちろん、他の友人が上に投稿した他の友人が参加するよりも、クロスが適用されるよりも使用される他の制限があります。
これは、すべてを説明する記事を紹介します。パフォーマンスの違いと参加を介して参加します。
SQL Server Crossが適用され、Auter Apply over over
この記事で示唆されているように、通常の結合操作(内側とクロス)については、それらの間にパフォーマンスの違いはありません。
このようなクエリを行う必要がある場合、使用法の違いが届きます。
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「バウンドできませんでした。」 ここでは、各行が読み取られると、値が関数に渡されます。私にはクールに聞こえます。 :)
これがCross ApplyとInnine結合を使用する理由として資格があるかどうかはわかりませんが、このクエリはCross Applyを使用したフォーラム投稿で私のために回答されたため、内部結合を使用して等量のメソッドがあるかどうかはわかりません。
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
終わり
適用演算子の本質は、From句のオペレーターの左側と右側の相関を可能にすることです。
結合とは対照的に、入力間の相関は許可されていません。
応用演算子の相関について話すと、右側に置くことができます。
- 派生テーブル - エイリアスと相関したサブクエリとして
- テーブルの価値のある関数 - パラメーターが左側を参照できるパラメーターを使用した概念ビュー
どちらも複数の列と行を返すことができます。
これはおそらく古い質問ですが、ロジックの再利用を簡素化し、結果の「チェーン」メカニズムを提供するために、私はまだクロスの力が適用されています。
SQLフィドルを提供しました。以下では、Cross Applyを使用してデータセットで複雑な論理操作を実行する方法の簡単な例を示しています。ここからより複雑な計算を外挿するのは難しくありません。