Frage

Was ist der Hauptzweck der Verwendung Kreuzbewerb?

Ich habe (vage durch Beiträge im Internet) gelesen, das cross apply kann bei der Auswahl großer Datensätze effizienter sein, wenn Sie eine Partitionierung haben. (Paging kommt mir in den Sinn)

Ich weiß das auch CROSS APPLY Es ist kein UDF als Rechtstabelle erforderlich.

In den meisten INNER JOIN Abfragen (Eins-zu-viele-Beziehungen), ich könnte sie neu schreiben, um sie zu verwenden CROSS APPLY, aber sie geben mir immer gleichwertige Ausführungspläne.

Kann mir jemand ein gutes Beispiel dafür geben, wann CROSS APPLY in jenen Fällen einen Unterschied machen, in denen INNER JOIN Wird auch funktionieren?


Bearbeiten:

Hier ist ein triviales Beispiel, bei dem die Ausführungspläne genau gleich sind. (Zeigen Sie mir einen, wo sie sich unterscheiden und wo cross apply ist schneller/effizienter)

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
War es hilfreich?

Lösung

Kann mir jemand ein gutes Beispiel dafür geben, wann Cross Apply einen Unterschied in den Fällen macht, in denen auch der innere Join funktioniert?

In dem Artikel in meinem Blog finden Sie einen detaillierten Leistungsvergleich:

CROSS APPLY Funktioniert besser bei Dingen, die kein einfaches haben JOIN Bedingung.

Dieser wählt 3 Letzte Aufzeichnungen von t2 für jeden Datensatz von 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

Es kann nicht leicht mit einem formuliert werden INNER JOIN Bedingung.

Sie könnten wahrscheinlich so etwas tun, indem Sie verwenden CTE's und Fensterfunktion:

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

, aber das ist weniger lesbar und wahrscheinlich weniger effizient.

Aktualisieren:

Gerade nachgeprüft.

master ist eine Tabelle von ungefähr 20,000,000 Aufzeichnungen mit a PRIMARY KEY an id.

Diese Abfrage:

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

läuft für fast 30 Sekunden, während dieser:

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

ist sofort.

Andere Tipps

cross apply ermöglicht es Ihnen manchmal, Dinge zu tun, mit denen Sie nicht tun können inner join.

Beispiel (ein Syntaxfehler):

select F.* from sys.objects O  
inner join dbo.myTableFun(O.name) F   
on F.schema_id= O.schema_id

Das ist ein Syntax-Fehler, weil, wenn es verwendet wird mit inner join, Tischfunktionen können nur dauern Variablen oder Konstanten als Parameter. (Dh der Parameter der Tabellenfunktion kann nicht von der Spalte einer anderen Tabelle abhängen.)

Jedoch:

select F.* from sys.objects O  
cross apply ( select * from dbo.myTableFun(O.name) ) F  
where F.schema_id= O.schema_id

Das ist legal.

Bearbeiten:Oder alternativ kürzere Syntax: (von Erike)

select F.* from sys.objects O  
cross apply dbo.myTableFun(O.name) F
where F.schema_id= O.schema_id

Bearbeiten:

Hinweis: Informix 12.10 xc2+ hat Lateral abgeleitete Tabellen und PostgreSQL (9,3+) hat Seitliche Unterabfragen Dies kann zu einem ähnlichen Effekt verwendet werden.

Bedenken Sie, dass Sie zwei Tabellen haben.

Meistertisch

x------x--------------------x
| Id   |        Name        |
x------x--------------------x
|  1   |          A         |
|  2   |          B         |
|  3   |          C         |
x------x--------------------x

Detailtabelle

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

Es gibt viele Situationen, in denen wir ersetzen müssen INNER JOIN mit CROSS APPLY.

1. Verbinden Sie zwei Tabellen basierend auf TOP n Ergebnisse

Überlegen Sie, ob wir auswählen müssen Id und Name aus Master und die letzten zwei Daten für jeden Id aus 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

Die obige Abfrage erzeugt das folgende Ergebnis.

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

Sehen Sie, es wurden Ergebnisse für die letzten beiden Daten mit den letzten beiden Datumen generiert Id und schloss sich diesen Aufzeichnungen nur in der äußeren Abfrage an an Id, was falsch ist. Um dies zu erreichen, müssen wir verwenden 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

und bildet das folgende Ergebnis.

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

So funktioniert das. Die Abfrage im Inneren CROSS APPLY kann auf die äußere Tabelle verweisen, wo INNER JOIN Kann dies nicht tun (es wirft Kompilierfehler aus). Wenn Sie die letzten beiden Daten finden, erfolgt die Verbindungen im Inneren CROSS APPLY dh,, WHERE M.ID=D.ID.

2. Wenn wir brauchen INNER JOIN Funktionalität mit Funktionen.

CROSS APPLY kann als Ersatz mit verwendet werden INNER JOIN Wenn wir Ergebnis von erzielen müssen Master Tisch und a function.

SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
CROSS APPLY dbo.FnGetQty(M.ID) C

Und hier ist die Funktion

CREATE FUNCTION FnGetQty 
(   
    @Id INT 
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT ID,PERIOD,QTY 
    FROM DETAILS
    WHERE ID=@Id
)

was das folgende Ergebnis erzeugte

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

Zusätzlicher Vorteil des Kreuzes gelten

APPLY kann als Ersatz für verwendet werden UNPIVOT. Entweder CROSS APPLY oder OUTER APPLY kann hier verwendet werden, die austauschbar sind.

Bedenken Sie, dass Sie die folgende Tabelle haben (benannt 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

Die Abfrage ist unten.

SELECT DISTINCT ID,DATES
FROM MYTABLE 
CROSS APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)

Was Ihnen das Ergebnis bringt

  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

Es scheint mir, dass Cross Apply eine bestimmte Lücke in der Arbeit mit berechneten Feldern in komplexen/verschachtelten Abfragen schließen und sie einfacher und lesbarer machen kann.

Einfaches Beispiel: Sie haben ein DOB und möchten mehrere altersbezogene Felder vorstellen, die auch auf andere Datenquellen (wie die Beschäftigung), wie Alter, Altersgruppe, Aperatur, MindestretirementDate usw., zur Verwendung in Ihrer Endbenutzeranwendung beruhen (Zum Beispiel Excel Pivottables).

Optionen sind begrenzt und selten elegant:

  • Join -Unterabfragen können nicht neue Werte in den Datensatz einführen, basierend auf Daten in der übergeordneten Abfrage (es muss für sich selbst stehen).

  • UDFs sind ordentlich, aber langsam, da sie dazu neigen, parallele Operationen zu verhindern. Und eine separate Einheit zu sein kann ein guter (weniger Code) oder eine schlechte (wo ist der Code) sein.

  • Junctionstische. Manchmal können sie arbeiten, aber bald genug, wobei Sie sich mit Tonnen von Gewerkschaften Unterabfragen anschließen. Großes Chaos.

  • Erstellen Sie eine weitere Einzelzweckansicht, vorausgesetzt, Ihre Berechnungen erfordern keine Daten, die über Ihre Hauptabfrage erhalten wurden.

  • Vermittlertabellen. Ja ... das funktioniert normalerweise und oft eine gute Option, da sie indexiert und schnell sind, aber die Leistung kann auch fallen, da die Aktualisierung von Anweisungen nicht parallel ist und nicht zulässt, dass Kaskadenformeln (Wiederverwendungsergebnisse) mehrere Felder innerhalb der aktualisiert werden können Gleiche Aussage. Und manchmal würde man es einfach vorziehen, Dinge in einem Pass zu machen.

  • Nistfragen. Ja, zu jedem Zeitpunkt können Sie eine Klammer auf Ihre gesamte Abfrage setzen und sie als Unterabfrage verwenden, auf die Sie Quelldaten und berechnete Felder gleichermaßen manipulieren können. Aber Sie können das nur so viel tun, bevor es hässlich wird. Sehr hässlich.

  • Code wiederholen. Was ist der größte Wert von 3 langen (Fall ... sonst ... Ende) Aussagen? Das wird lesbar sein!

    • Sagen Sie Ihren Kunden, sie sollen die verdammten Dinge selbst berechnen.

Habe ich etwas verpasst? Wahrscheinlich können Sie gerne kommentieren. Aber hey, Cross bewerben ist wie ein Glücksfall in solchen Situationen: Sie fügen einfach eine einfache hinzu CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl und voilà! Ihr neues Feld ist jetzt für die Verwendung praktisch bereit, wie es immer in Ihren Quelldaten da war.

Werte, die durch Cross Apply eingeführt werden können ... können ...

  • werden verwendet, um ein oder mehrere berechnete Felder zu erstellen, ohne dass der Mix Leistung, Komplexität oder Lesbarkeitsprobleme hinzufügen
  • Wie bei Joins können mehrere nachfolgende Kreuzungsanlagen auf sich selbst verweisen: CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
  • Sie können Werte verwenden, die durch ein Kreuzantrag in nachfolgenden Verbindungsbedingungen eingeführt werden
  • Als Bonus gibt es den Funktionsaspekt der Tabellenwerte

Dang, es gibt nichts, was sie nicht tun können!

Cross Apply funktioniert auch gut mit einem XML -Feld. Wenn Sie Knotenwerte in Kombination mit anderen Feldern auswählen möchten.

Zum Beispiel, wenn Sie eine Tabelle mit XML enthalten

<root>
    <subnode1>
       <some_node value="1" />
       <some_node value="2" />
       <some_node value="3" />
       <some_node value="4" />
    </subnode1>
</root>

Verwenden der Abfrage

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

Wird ein Ergebnis zurückgeben

xt_id root_attribute_value node_attribute_value lt_name
----------------------------------------------------------------------
1     test1            1                    Benefits
1     test1            4                    FINRPTCOMPANY

Dies wurde bereits technisch sehr gut beantwortet, aber lassen Sie mich ein konkretes Beispiel dafür geben, wie äußerst nützlich ist:

Nehmen wir an, Sie haben zwei Tische, Kunden und Bestellung. Kunden haben viele Bestellungen.

Ich möchte eine Ansicht erstellen, die mir Details über Kunden und die neueste Bestellung gibt, die sie erteilt haben. Bei nur gemeinsamen Anschlüssen würde dies einige Selbstjoins und Aggregation erfordern, die nicht hübsch sind. Aber mit Kreuzung ist es super einfach:

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

Cross Apply kann verwendet werden, um Unterabfragen zu ersetzen, bei denen Sie eine Spalte der Unterabfrage benötigen

Unterabfrage

select * from person p where
p.companyId in(select c.companyId from company c where c.companyname like '%yyy%')

Hier kann ich nicht in der Lage sein, die Spalten der Firmentabelle auszuwählen. Mit Cross Apply bewerben

select P.*,T.CompanyName
from Person p
cross apply (
    select *
    from Company C
    where p.companyid = c.companyId and c.CompanyName like '%yyy%'
) T

Ich denke, es sollte Lesbarkeit sein;)

Cross Apply ist für Personen, die lesen, etwas einzigartig, um ihnen mitzuteilen, dass ein UDF verwendet wird, der auf jede Zeile aus der Tabelle links angewendet wird.

Natürlich gibt es noch andere Einschränkungen, bei denen ein Kreuzbewerb besser verwendet wird, als sich der oben veröffentlichten Freunde anzuschließen.

Hier ist ein Artikel, der alles erklärt, mit ihrem Leistungsunterschied und ihrer Nutzung über Joins.

SQL Server Cross Application und Outer bewerben Sie sich über gemeinsame Anschlüsse

Wie in diesem Artikel vorgeschlagen, gibt es keinen Leistungsunterschied zwischen ihnen für normale Join -Operationen (innerlich und Kreuz).

enter image description here

Der Verwendungsunterschied kommt, wenn Sie eine solche Frage durchführen müssen:

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)

Das heißt, wenn Sie sich auf die Funktion beziehen müssen. Dies kann nicht mit dem inneren Join durchgeführt werden, was Ihnen den Fehler ergibt "Die mehrteilige Kennung" D.-DepartmentId "konnte nicht gebunden werden." Hier wird der Wert an die Funktion übergeben, wenn jede Zeile gelesen wird. Klingt cool für mich. :)

Nun, ich bin mir nicht sicher, ob dies als Grund für die Verwendung von Cross Apply im Vergleich zu innerem Join qualifiziert ist, aber diese Abfrage wurde für mich in einem Forum -Post mit Cross Apply beantwortet. Ich bin mir also nicht sicher, ob es eine gleiche Methode mit innerer Join gibt:

Create PROCEDURE [dbo].[Message_FindHighestMatches]

-- Declare the Topical Neighborhood
@TopicalNeighborhood nchar(255)

Wie beginnen

-- 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

ENDE

Das Wesen des Antragsbetreibers besteht darin, die Korrelation zwischen der linken und der rechten Seite des Bedieners in der From -Klausel zu ermöglichen.

Im Gegensatz zur Verbindung ist die Korrelation zwischen Eingängen nicht zulässig.

Wenn wir über Korrelation im Antragsbetreiber sprechen, meine ich auf der rechten Seite, können wir einsetzen:

  • eine abgeleitete Tabelle - als korrelierte Unterabfrage mit einem Alias
  • Eine Tabelle wertvolle Funktion - eine konzeptionelle Ansicht mit Parametern, wobei der Parameter auf die linke Seite verweisen kann

Beide können mehrere Spalten und Zeilen zurückgeben.

Dies ist vielleicht eine alte Frage, aber ich liebe immer noch die Kraft des Kreuzes, die Wiederverwendung der Logik zu vereinfachen und einen "Verkettungsmechanismus" für die Ergebnisse bereitzustellen.

Ich habe unten eine SQL -Geige bereitgestellt, die ein einfaches Beispiel dafür zeigt, wie Sie Cross Apply verwenden können, um komplexe logische Operationen in Ihrem Datensatz auszuführen, ohne dass überhaupt eine Sache unordentlich ist. Es ist nicht schwer, von hier aus komplexere Berechnungen zu extrapolieren.

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top