سؤال

ما هو الغرض الرئيسي من استخدام عبر تطبيق?

لقد قرأت (غامضة ، من خلال وظيفة على الإنترنت) 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 يعمل بشكل أفضل على الأشياء التي ليس لها بسيطة 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 هو جدول حول 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

هذا قانونية.

يحرر:أو بدلا من ذلك، بناء جملة أقصر: (عن طريق إيق

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+) لديه السدود الجانبية والتي يمكن استخدامها تأثير مماثل.

النظر في أن لديك طاولتين.

الجدول الرئيسي

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 النتائج

النظر في ما إذا كنا بحاجة إلى الاختيار Id و Name من Master وآخر تواريخ لكل منهما 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

انظر، لقد ولدت نتائج لآخر تواريخ تواريخ مع آخر تاريخين 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 لا يمكن القيام بذلك (يرمي خطأ في الترجمة). عند العثور على آخر تواريخ، يتم الانضمام في الداخل CROSS APPLY بمعنى آخر، WHERE M.ID=D.ID.

2. عندما نحتاج INNER JOIN وظيفة باستخدام الوظائف.

CROSS APPLY يمكن استخدامها كبديل مع INNER JOIN عندما نحتاج إلى الحصول على نتيجة من Master الجدول و 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

ميزة إضافية من التقاطع

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

يبدو لي أن عبر تطبيق يمكن ملء فجوة معينة عند العمل مع الحقول المحسوبة في مجمع/المتداخلة الاستعلامات, وجعلها أكثر بساطة و أكثر قابلية للقراءة.

مثال بسيط:لديك دوب و تريد تقديم متعددة سن المجالات ذات الصلة التي سوف تعتمد أيضا على مصادر البيانات الأخرى (مثل العمالة) ، مثل العمر ، AgeGroup, AgeAtHiring, MinimumRetirementDate ، إلخ.للاستخدام في التطبيقات للمستخدم النهائي (Excel PivotTables ، على سبيل المثال).

الخيارات محدودة و نادرا أنيقة:

  • الانضمام إلى الاستعلامات الفرعية لا يمكن إدخال قيم جديدة في مجموعة البيانات استنادا إلى البيانات في الأصل الاستعلام (يجب أن تقف على قدميها).

  • UDFs أنيق ، ولكن بطيئة كما أنها تميل إلى منع عمليات موازية.ويجري كيان منفصل يمكن أن تكون جيدة (رمز أقل) أو سيئة (حيث هو رمز) الشيء.

  • تقاطع الجداول.في بعض الأحيان أنها يمكن أن تعمل ، ولكن قريبا بما فيه الكفاية أنت الانضمام الاستعلامات الفرعية مع طن من النقابات.فوضى كبيرة.

  • خلق آخر واحد الغرض الرأي ، على افتراض الحسابات الخاصة بك لا تحتاج إلى البيانات التي تم الحصول عليها في منتصف الطريق من خلال الرئيسي الخاص بك الاستعلام.

  • الوسيط الجداول.نعم...التي عادة ما يعمل ، وغالبا ما تكون خيارا جيدا لأنها يمكن فهرستها و سريع لكن الأداء أيضا إسقاط بسبب تحديث البيانات لا يجري بالتوازي وعدم السماح تتالي الصيغ (إعادة استخدام النتائج) لتحديث العديد من المجالات داخل نفس البيان.وأحيانا كنت تفضل أن تفعل أشياء في مرور واحد.

  • التعشيش الاستعلامات.نعم في أي وقت يمكنك وضع أقواس على كامل الاستعلام واستخدامه بمثابة فرعي التي يمكنك التعامل مع مصدر البيانات والحقول المحسوبة على حد سواء.ولكن يمكنك فقط القيام بذلك كثيرا قبل أن يحصل على القبيح.قبيحة جدا.

  • تكرار التعليمات البرمجية.ما هو أعظم قيمة من 3 طويلة (القضية...آخر...END) التصريحات ؟ التي سوف تكون قابلة للقراءة!

    • أخبر العملاء لحساب الأشياء نفسها.

هل فاتني شيء ؟ ربما لذلك لا تتردد في التعليق.ولكن مهلا ، عبر تطبيق مثل هبة من السماء في مثل هذه الحالات:يمكنك فقط إضافة بسيطة CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl وفويلا!الجديد الخاص بك المجال هي الآن جاهزة للاستخدام عمليا مثل أنه كان دائما هناك في البيانات المصدر.

القيم التي أدخلت عبر تطبيق يمكن...

  • يمكن استخدامها لإنشاء واحد أو أكثر من الحقول المحسوبة دون إضافة الأداء تعقيد أو القراءة القضايا إلى المزيج
  • مثل ينضم عدة اللاحقة عبر تطبيق بيانات يمكن الرجوع إلى أنفسهم: CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
  • يمكنك استخدام القيم التي أدخلتها عبر تطبيق اللاحقة شروط الانضمام
  • علاوة على ذلك هناك قيم الجدول وظيفة الجانب

اللعنة, لا يوجد شيء أنها لا يمكن أن تفعل!

عبر تطبيق يعمل بشكل جيد مع حقل 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 Customer
CROSS APPLY (
  SELECT TOP 1 *
  FROM Order
  WHERE Order.CustomerId = Customer.CustomerId
  ORDER BY OrderDate DESC
) T

يمكن استخدام Cross Supply لتحل محل Squarquery حيث تحتاج إلى عمود من الاسمية الاسمية

Suacquery.

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

أعتقد أنه يجب أن تكون سريدا؛)

SPRICE STUP سيكون فريدا إلى حد ما بالنسبة للأشخاص الذين يقرؤون أن يخبرهم أن UDF يستخدم سيتم تطبيقه على كل صف من الجدول على اليسار.

فكورسي، هناك قيود أخرى حيث يتم استخدام التقاطع أفضل من الانضمام الذي أرسله الأصدقاء الآخرون أعلاه.

فيما يلي مقال يشرح كل شيء، مع اختلاف أدائه واستخدامه على الانضمام.

SQL Server 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)

وهذا هو، عندما تضطر إلى الارتباط مع وظيفة. لا يمكن القيام بذلك باستخدام الانضمام الداخلي، والتي من شأنها أن تعطيك الخطأ "المعرف متعدد الأجزاء" لا يمكن ربطه ". هنا يتم تمرير القيمة إلى الوظيفة حيث يتم قراءة كل صف. يبدو رائع لي. :)

حسنا، لست متأكدا مما إذا كان هذا مؤهلا كسبب لاستخدامه عبر التطبيق مقابل الانضمام مقابل الانضمام مقابل ذلك، ولكن تمت الرد على هذا الاستعلام لي في منشور المنتدى باستخدام Cross Supply، لذلك لست متأكدا مما إذا كان هناك طريقة تعاليل باستخدام Inner Join:

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

نهاية

جوهر تشغيل المشغل هو السماح بالارتباط بين الجانب الأيسر والأيمن من المشغل في جملة من البند.

على عكس الانضمام، لا يسمح بالارتباط بين المدخلات.

تحدث عن الارتباط في تطبيق المشغل، أقصد على الجانب الأيمن الذي يمكننا وضعه:

  • جدول مشتق - كعقود استقبال مرتبط مع اسم مستعار
  • وظيفة قيمة الجدول - عرض مفاهيمي مع المعلمات، حيث يمكن للمعلمة الرجوع إلى الجانب الأيسر

كلاهما يمكن أن يرجع أعمدة متعددة والصفوف.

ربما يكون هذا سؤالا قديما، لكن ما زلت أحب قوة الصليب تنطبق على تبسيط إعادة استخدام المنطق وتوفير آلية "تسلسل" للنتائج.

لقد قدمت كمان SQL أدناه والتي تظهر مثالا بسيطا على كيفية استخدام Cross Supply لإجراء عمليات منطقية معقدة على مجموعة البيانات الخاصة بك دون أن تحصل على أشياء فوضوي. ليس من الصعب استقراء من هنا حسابات أكثر تعقيدا.

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top