تفاوت CROSS APPLY و CROSS JOIN

تفاوت CROSS APPLY و CROSS JOIN

نوشته شده توسط: تورج عزیزی
۰۴ شهریور ۱۳۹۴
زمان مطالعه: 16 دقیقه
۴
(۳)

مقدمه

سلام؛ انتظار می رود خواننده این مقاله با مفاهیم Derived Table و Subquery ها و دستور APPLY آشنا باشد.

قبل از اینکه بخواهیم پی به تفاوت CROSS APPLY و CROSS JOIN ببریم، باید کمی در مورد Derived Table ها و Correlated Sub-query ها و قلمر دید این دو عبارت صحبت کنم.
دوره کوئری نویسی نیک آموز
یک Derived Table  اصطلاحاً Self-Contained است یعنی تمام جداول و ستون های SELECT پدر در Derived Table غیر قابل دستیابی است. به مثال زیر توجه کنید:
select A.*, b.X
from A
cross join (select B.X from B where B.Val=A.Val) b
منظور از SELECT پدر همان SELECT ای است که یک لایه بالاتر از Derived Table است.
این دستور نامعتبر است چون A.Val در قلمرو دید Derived Table نیست (ارزیابی Derived Table مستقل از سایر جداول در کوئری انجام می شود). برای محدود کردن خروجی کوئری بالا به طوریکه A.Val مساوی با B.Val باشد باید از شرط WHERE استفاده کنیم:
select A.*, b.X
from A
cross join (select * from B) b
where A.Val = b.Val

 قانون قلمرو دید Derived Table ها محدود به  CROSS JOIN نیست و در همه انواع Join این قانون بر قرار است: OUTER و INNER و حتی UNION.
این قانون در تضاد با قلمرو دید Correlated Sub-query است. در Correlated Sub-query جداول و ستون های SELECT پدر در قلمرو دید Sub-query است. Sub-query به ازای هر ردیف کوئری اجرا می شود بنابراین سایر ستون ها و جداول در دسترس هستند:

select A.*, (select B.X from B where B.Val=A.Val) as X
from A

یک راه ساده برای درک تفاوت بین CROSS APPLY و CROSS JOIN  دانستن همین اختلاف در قلمرو دید است.  CROSS JOIN با یک Derived Table جوین می شود اما CROSS APPLY بر خلاف ظاهرش در حقیقت یک Correlated Sub-query را روی هر ردیف اعمال می کند.
بنابراین می توانیم کوئری اول را به این شکل بنویسیم:

select A.*, b.X
from A
cross apply (select B.X from B where B.Val=A.Val) b

 از آنجایی که ما یک CROSS APPLY اجرا کردیم نه یک CROSS JOIN پس A.Val در قلمرو دید ماست و کوئری به درستی اجرا می شود.
همین قوانین در مورد Table Valued Function ها هم صادق است:

select A.*, B.X
from A
cross join dbo.UDF(A.Val) B

باز هم این کوئری معتبر نیست چون A.Val خارج از قلمرو دید است و بهترین کاری که تا قبل از ۲۰۰۵ می توان کرد استفاده از یک Correlated Sub-query است:

select A.*, (select X from dbo.UDF(A.Val)) X
from A

اما اگر تابع UDF بالا بیش از یک ردیف بر گرداند خطا می دهد. از نسخه ۲۰۰۵ به بعد اکنون می توانیم اینطور بنویسیم:

select A.*, b.X
from A
cross apply dbo.UDF(A.Val) b

از نظر Performance به طور کلی APPLY از JOIN ضعیف تر و Correlated Sub-query ضعیف تر از Derived Table است. با این وجود چرا باید از APPLY به جای Correlated Sub-query استفاده کنیم؟ چون قدرتمند تر است!

CROSS APPLY می تواند چند ردیف را برگرداند

بر خلاف Correlated Sub-query، دستور CROSS APPLY با چند ردیف کار می کند. این کار به ما اجازه می دهد تا یک جدول را به یک تابع مانند ParseCSV که خروجی اش چند ردیف است JOIN کنیم:

select A.ID, b.Val
from A
cross apply dbo.ParseCSV(A.CSV) b

وقتی تابع ParseCSV() چندین ردیف را برمی گرداند مانند این است که ما خروجی تابع را با جدول A  جوین کرده ایم و هر رکورد جدول A را به ازای خروجی تابع تکرار کرده ایم. کاری که باCorrelated Sub-query نمی توانستیم انجام دهیم.

CROSS APPLY می تواند چند ستون را برگرداند

در یک  Correlated Sub-query ما فقط می توانیم یک مقدار برگردانیم. مثلاً اگر بخواهیم یک مقدار running sum برگردانیم می توانیم به این شکل عمل کنیم:
select o.*,
(select sum(Amount) from Order o
where p.OrderDate <= o.OrderDate) as RunningSum
from Order o
و اگر بخواهیم یک دیگر بر اساس شرطی خاص برگردانیم مثلاً OrderCode های یکسان باید به این شکل عمل کنیم:
 select o.*,
(select sum(Amount) from Order o
where p.OrderDate <= o.OrderDate) as RunningSum,
(select sum(Amount) from Order o
where p.OrderCode = o.OrderCode and p.OrderDate <= o.OrderDate) as SameCode
from Order o
اما با استفاده از CROSS APPLY می توانیم ساده تر عمل کنیم

select o.*, rs.RunningSum, rs.SameCode
from Order o
cross apply
(
select
sum(Amount) as RunningSum,
sum(case when p.OrderCode = o.OrderCode then Amount else 0 end) as SameCode
from Order P
where P.OrderDate <= O.OrderDate
در کد بالا مزیت برگشت چند ستون به جای یک ستون را می بینید مانند Derived Table و همینطور قابلیت ارجاع به ستون های SELECT بیرونی.
CROSS APPLY همچنین امکان رجوع به ردیف قبلی را به سادگی ممکن می کند:

 select o.*, prev.*
from Order o
cross apply
(
select top 1 *
from Order P where P.OrderDate < O.OrderDate
order by OrderDate DESC
) prev
توجه کنید که CROSS APPLY در کد بالا به ازای سفارشاتی که یک روز قبل از تاریخشان سفارشی ثبت نشده نشان داده نمی شوند برای رفع این مشکل باید از OUTER APPLY استفاده کرد:

 select o.*, prev.*
from Order o
outer apply
(
select top 1 *
from Order P where P.OrderDate < O.OrderDate
order by OrderDate DESC
) prev

 

برای بدست آوردن اطلاعات بیش‌تر در مورد دیگر دستورات SQL ، به مقاله زیر مراجعه کنید.
 
دستورهای SQL Server

چه رتبه ای می‌دهید؟

میانگین ۴ / ۵. از مجموع ۳

اولین نفر باش

title sign
معرفی نویسنده
تورج عزیزی
مقالات
17 مقاله توسط این نویسنده
محصولات
0 دوره توسط این نویسنده
تورج عزیزی
پروفایل نویسنده
title sign
دیدگاه کاربران

    • سلام
      اگر کمی به معنی خود کلمه apply توجه کنید متوجه کلیت قضیه می شید این کلمه کلیدی اجازه میده روی تمام رکورد های سمت چپ apply عملیاتی رو انجام بدین و خروجی رو به ازای هر ردیف تولید کنید که این خروجی به فرم ردیف هست و هر ردیف میتونه حداقل یک ستون یا بیشتر رو داشته باشه … حالا ممکنه به ازای یک ردیف خاص هیچ خروجی نداشته باشیم و به ازای یک ردیف دیگه چند رکورد داشته باشیم اگر بخواهیم ردیف های سمت چپ apply بدون توجه به خروجی apply همیشه تو خروجی نهایی ظاهر بشن باید از outer استفاده کنیم در غیر این صورت cross استفاده میشه…
      ضمنا در cross join شما صرفا یک ضرب دکارتی دارید اما هدف از apply این هست که رکوردهایی با استفاده از رکورد های سمت چپ apply تولید شوند و در این فرایند تولید معمولا از فیلدهای سمت چپ apply در سمت راست استفاده میشود….
      امید دارم واضح گفته باشم

      ۱
هر روز یک ایمیل، هر روز یک درس
آموزش SQL Server بصورت رایگان
همین حالا فرم زیر را تکمیل کنید
دانلود رایگان جلسه اول
نیک آموز علاوه بر آموزش، پروژه‌های بزرگ در حوزه هوش تجاری و دیتا انجام می‌دهد.
close-link
وبینار رایگان SQL Server؛ مسیری به سوی فرصت‌های شغلی بی‌شمار       پنج‌شنبه 30 فرودین ساعت 15
ثبت نام رایگان
close-image