¿Cuándo debo usar la aplicación cruzada sobre la unión interna?


925

¿Cuál es el propósito principal de usar CROSS APPLY ?

He leído (vagamente, a través de publicaciones en Internet) que cross applypuede ser más eficiente al seleccionar sobre conjuntos de datos grandes si está particionando. (La paginación viene a la mente)

También sé que CROSS APPLYno requiere un UDF como la tabla correcta.

En la mayoría de las INNER JOINconsultas (relaciones uno a muchos), podría reescribirlas para usarlas CROSS APPLY, pero siempre me dan planes de ejecución equivalentes.

¿Alguien puede darme un buen ejemplo de cuándo CROSS APPLYmarca la diferencia en aquellos casos en INNER JOINque funcionará también?


Editar:

Aquí hay un ejemplo trivial, donde los planes de ejecución son exactamente los mismos. (Muéstrame uno donde difieren y donde cross applyes más rápido / más eficiente)

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

50
Sé que esto es INCLUSO MÁS PELIGROSO de mí, pero 'rendimiento' es definitivamente una palabra. Simplemente no está relacionado con la eficiencia.
Rire1979

2
Es muy útil para sql xquery. Mira esto .
ARZ

3
Parece que el uso de "unión de bucle interno" estaría muy cerca de la aplicación cruzada. Me gustaría que tu ejemplo detallara qué sugerencia de combinación era equivalente. Solo decir unir puede resultar en interno / bucle / fusión o incluso "otro" porque puede reorganizarse con otras uniones.
crokusek

3
Cuando la unión creará muchas filas, pero solo necesita evaluar una unión de fila a la vez. Tenía un caso en el que necesitaba unirme en una mesa con más de 100 millones de filas y no había suficiente memoria. Así que fui cursor para reducir la huella de memoria. Desde el cursor, apliqué de forma cruzada como huella de memoria administrada y era 1/3 más rápido que el cursor.
paparazzo

10
CROSS APPLYtiene su uso obvio al permitir que un conjunto dependa de otro (a diferencia del JOINoperador), pero eso no tiene un costo: se comporta como una función que opera sobre cada miembro del conjunto izquierdo , por lo que, en términos de SQL Server, siempre realice un Loop Join, que casi nunca es la mejor manera de unir conjuntos. Por lo tanto, úselo APPLYcuando lo necesite, pero no lo use en exceso JOIN.
Gerardo Lima

Respuestas:


668

¿Alguien puede darme un buen ejemplo de cuándo CROSS APPLY marca la diferencia en aquellos casos en que INNER JOIN también funcionará?

Vea el artículo en mi blog para una comparación detallada del rendimiento:

CROSS APPLYfunciona mejor en cosas que no tienen una JOINcondición simple .

Éste selecciona los 3últimos registros de t2cada registro de 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

No se puede formular fácilmente con una INNER JOINcondición.

Probablemente podría hacer algo así usando la función CTE's y ventana:

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

, pero esto es menos legible y probablemente menos eficiente.

Actualizar:

Acabo de revisarlo.

masteres una tabla de 20,000,000registros con un PRIMARY KEYencendido id.

Esta consulta:

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

corre por casi 30segundos, mientras que este:

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

es instantáneo


2
Ver el final del enlace de Ariel. Una consulta row_number () es igual de buena y ni siquiera requiere una unión. Por lo tanto, no creo que deba usar la aplicación cruzada para esta situación (seleccione el top 3, partición por t1.id).
Jeff Meatball Yang

375
Aunque esta es la respuesta más popular, no creo que responda a la pregunta real "¿Cuál es el propósito principal de usar CROSS APPLY?". El objetivo principal es permitir que las funciones de la tabla con parámetros se ejecuten una vez por fila y luego se unan a los resultados.
MikeKulls

55
@ Mike: ¿cómo se llama a TVFcon INNER JOIN?
Quassnoi

15
@MikeKulls Sí, pero el OP no solicitó el propósito principal del uso CROSS APPLY, preguntó cuándo elegirlo y INNER JOINcuándo funcionaría también.
ErikE

8
Vale la pena mencionar que esto se llama lateral joinSQL estándar (ANSI)
a_horse_with_no_name

198

cross applya veces te permite hacer cosas con las que no puedes hacer inner join.

Ejemplo (un error de sintaxis):

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

Este es un error de sintaxis porque, cuando se usa con inner join, las funciones de tabla solo pueden tomar variables o constantes como parámetros. (Es decir, el parámetro de la función de tabla no puede depender de la columna de otra tabla).

Sin embargo:

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

Esto es legal

Editar: O, alternativamente, sintaxis más corta: (por ErikE)

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

Editar:

Nota: Informix 12.10 xC2 + tiene tablas derivadas laterales y Postgresql (9.3+) tiene subconsultas laterales que se pueden usar con un efecto similar.


11
Creo que este es el razonamiento detrás de por qué tenemos una aplicación cruzada. Si revisa el siguiente enlace, esto es lo primero que dice MS sobre la aplicación cruzada. Puede tener otros usos, pero creo que esta es la razón por la que se introdujo. Sin ella, las funciones de tabla no serían utilizables en muchas situaciones. technet.microsoft.com/en-us/library/ms175156.aspx
MikeKulls

la aplicación cruzada también produce un buen plan de ejecución cuando se combina con funciones de tabla en línea mientras se mantiene la modularidad que tanto se necesita.
nurettin

14
No es SELECTnecesario dentro del CROSS APPLY. Por favor, intente CROSS APPLY dbo.myTableFun(O.name) F.
ErikE

1
@ErikE seguro, siempre puedes usar la sintaxis menos flexible para aplicar en forma cruzada. Estaba mostrando la versión más generalizada que a veces se puede usar para evitar incorporar columnas difíciles de calcular en la consulta.
nurettin

2
La combinación interna de @Bolu no funcionará si el parámetro de función de la tabla depende de la columna de otra tabla (también conocida como referencia externa) en la selección externa. Funcionará si el parámetro de la función de tabla es un literal o una variable. La aplicación cruzada funcionará en ambos casos.
nurettin

175

Considera que tienes dos mesas.

MESA PRINCIPAL

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

TABLA DE DETALLES

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

Hay muchas situaciones en las que debemos reemplazar INNER JOINconCROSS APPLY .

1. Unir dos tablas basadas en TOP n resultados.

Considere si necesitamos seleccionar Idy Namedesde Mastery las dos últimas fechas para cada uno Idde 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

La consulta anterior genera el siguiente resultado.

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

Vea, generó resultados para las últimas dos fechas con las últimas dos fechas Idy luego unió estos registros solo en la consulta externa Id, lo cual es incorrecto. Esto debería devolver tanto Ids1 como 2, pero solo devolvió 1 porque 1 tiene las dos últimas fechas. Para lograr esto, necesitamos usar 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

y forma el siguiente resultado.

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

Así es como funciona. La consulta interna CROSS APPLYpuede hacer referencia a la tabla externa, donde INNER JOINno puede hacer esto (arroja un error de compilación). Al encontrar las dos últimas fechas, la unión se realiza dentro CROSS APPLY, es decir,WHERE M.ID=D.ID .

2. Cuando necesitamos INNER JOINfuncionalidad usando funciones.

CROSS APPLYse puede usar como un reemplazo INNER JOINcuando necesitamos obtener resultados de la Mastertabla y a function.

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

Y aquí está la función.

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

que generó el siguiente resultado

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

VENTAJA ADICIONAL DE APLICACIÓN CRUZADA

APPLYse puede usar como reemplazo de UNPIVOT. Ya seaCROSS APPLY o OUTER APPLYpuede usarse aquí, que son intercambiables.

Considere que tiene la siguiente tabla (nombrada 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

La consulta está abajo.

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

que te trae el resultado

  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

44
Excelente ejemplo con los registros 2 vs 4 y me ayudó a entender el contexto en el que esto sería necesario.
Trnelson

13
Esta respuesta demuestra que realmente vale la pena desplazarse hacia abajo en la página en lugar de solo elegir la aceptada.
Mostafa Armandi

2
El mejor ejemplo hasta ahora para explicar el uso de APLICAR ... He leído muchas publicaciones y me doy cuenta de que esta explicación borra la imagen como agua. Muchas gracias hermano.
AG7

1
Para el punto 1, donde tenemos 2 filas para la ID 1 en lugar de 4 filas para la ID 1, 2. ¿No usaríamos una combinación izquierda en su lugar?
Joseph Cho

43

Me parece que CROSS APPLY puede llenar un cierto vacío cuando se trabaja con campos calculados en consultas complejas / anidadas, y hacerlos más simples y legibles.

Ejemplo simple: tiene un DoB y desea presentar múltiples campos relacionados con la edad que también dependerán de otras fuentes de datos (como el empleo), como Age, AgeGroup, AgeAtHiring, MinimumRetirementDate, etc. para usar en su aplicación de usuario final (Tablas dinámicas de Excel, por ejemplo).

Las opciones son limitadas y rara vez elegantes:

  • Las subconsultas JOIN no pueden introducir nuevos valores en el conjunto de datos en función de los datos de la consulta principal (debe ser independiente).

  • Los UDF son limpios, pero lentos, ya que tienden a evitar operaciones paralelas. Y ser una entidad separada puede ser algo bueno (menos código) o malo (dónde está el código).

  • Mesas de unión. A veces pueden funcionar, pero pronto te unes a subconsultas con toneladas de UNION. Gran desorden.

  • Cree otra vista de un solo propósito, suponiendo que sus cálculos no requieren datos obtenidos a mitad de su consulta principal.

  • Mesas intermedias. Sí ... eso generalmente funciona, y a menudo es una buena opción, ya que pueden indexarse ​​y ser rápidos, pero el rendimiento también puede disminuir debido a que las declaraciones de ACTUALIZACIÓN no son paralelas y no permiten que las fórmulas en cascada (reutilizar resultados) actualicen varios campos dentro del misma declaración Y a veces prefieres hacer las cosas de una sola vez.

  • Consultas de anidamiento. Sí, en cualquier momento puede poner paréntesis en toda su consulta y usarlo como una subconsulta sobre la cual puede manipular los datos de origen y los campos calculados por igual. Pero solo puedes hacer esto mucho antes de que se ponga feo. Muy feo.

  • Repetir código. ¿Cuál es el mayor valor de 3 declaraciones largas (CASE ... ELSE ... END)? ¡Eso será legible!

    • Dile a tus clientes que calculen las malditas cosas ellos mismos.

¿Me he perdido algo? Probablemente, así que siéntete libre de comentar. Pero bueno, CROSS APPLY es como un regalo del cielo en tales situaciones: solo agregas un simpleCROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl y listo! Su nuevo campo ahora está listo para usar prácticamente como siempre había estado allí en sus datos de origen.

Los valores introducidos a través de CROSS APPLY pueden ...

  • se puede usar para crear uno o varios campos calculados sin agregar problemas de rendimiento, complejidad o legibilidad a la mezcla
  • Al igual que con JOIN, varias declaraciones CROSS APPLY posteriores pueden referirse a sí mismas: CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
  • puede usar valores introducidos por una APLICACIÓN CRUZADA en condiciones subsiguientes de JOIN
  • Como beneficio adicional, está el aspecto de función con valores de tabla

¡Dang, no hay nada que no puedan hacer!


1
Este es un gran +1 de mi parte, ya que me sorprende que no se mencione con más frecuencia. ¿Quizás podría extender este ejemplo para mostrar cómo puede realizar cálculos "de procedimiento" en la cadena de valores derivados? Por ejemplo: APLICACIÓN CRUZADA (seleccione crossTbl.value * tbl.multiplier como Multiplicado) multiTbl - CROSS APPLY (seleccione multiTbl.Multiplied / tbl.DerivativeRatio como Derived) derivadoTbl - etc ...
mrmillsy

1
¿Más información / ejemplos sobre cómo usar Cross Apply como reemplazo de CASE..ELSE..END?
przemo_li

3
@przemo_li APPLY puede usarse para almacenar el resultado de una declaración de caso (entre otras cosas) para referirse a él. Una estructura podría ser algo así como: SELECCIONAR CASO cuando subquery.intermediateResult> 0 LUEGO "sí" ELSE "no" FINALIZAR DE ALGUNA TABLA EXTERIOR APLICAR (seleccione CASO ... FINALIZAR ... ELSE como resultado intermedio) como subconsulta.
mtone

14

La aplicación cruzada también funciona bien con un campo XML. Si desea seleccionar valores de nodo en combinación con otros campos.

Por ejemplo, si tiene una tabla que contiene algunos xml

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

Usando la consulta

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

Devolverá un resultado

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

13

Esto ya se ha respondido muy bien técnicamente, pero permítanme dar un ejemplo concreto de cómo es extremadamente útil:

Digamos que tiene dos tablas, Cliente y Pedido. Los clientes tienen muchos pedidos.

Quiero crear una vista que me brinde detalles sobre los clientes y el pedido más reciente que han realizado. Con solo JOINS, esto requeriría algunas autouniones y agregaciones que no son bonitas. Pero con Cross Apply, es súper fácil:

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

7

La aplicación cruzada se puede utilizar para reemplazar las subconsultas donde necesita una columna de la subconsulta

subconsulta

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

aquí no podré seleccionar las columnas de la tabla de la compañía, así que, usando la aplicación cruzada

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

5

Supongo que debería ser legibilidad;)

CROSS APPLY será algo único para las personas que leen y les digan que se está utilizando un UDF que se aplicará a cada fila de la tabla de la izquierda.

Por supuesto, hay otras limitaciones en las que una APLICACIÓN CRUZADA se usa mejor que ÚNETE que otros amigos han publicado anteriormente.


4

Aquí hay un artículo que lo explica todo, con su diferencia de rendimiento y uso sobre JOINS.

SQL Server CROSS APPLY y OUTER APPLY over JOINS

Como se sugiere en este artículo, no hay diferencia de rendimiento entre ellos para las operaciones de unión normales (INTERIOR Y CRUZ).

ingrese la descripción de la imagen aquí

La diferencia de uso llega cuando tienes que hacer una consulta como esta:

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)

Es decir, cuando tienes que relacionarte con la función. Esto no se puede hacer utilizando INNER JOIN, que le daría el error "El identificador de varias partes" D.DepartmentID "no se pudo vincular". Aquí el valor se pasa a la función a medida que se lee cada fila. A mí me parece genial. :)


3

Bueno, no estoy seguro de si esto califica como una razón para usar Cross Apply versus Inner Join, pero esta consulta fue respondida por mí en una publicación del foro usando Cross Apply, por lo que no estoy seguro de si hay un método igual usando Inner Join:

Create PROCEDURE [dbo].[Message_FindHighestMatches]

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

COMO COMIENZA

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

FINAL


3

La esencia del operador APLICAR es permitir la correlación entre el lado izquierdo y derecho del operador en la cláusula FROM.

A diferencia de JOIN, la correlación entre entradas no está permitida.

Hablando de correlación en el operador APLICAR, quiero decir en el lado derecho podemos poner:

  • una tabla derivada - como una subconsulta correlacionada con un alias
  • una función con valores de tabla: una vista conceptual con parámetros, donde el parámetro puede referirse al lado izquierdo

Ambos pueden devolver múltiples columnas y filas.


2

Esta es quizás una vieja pregunta, pero todavía me encanta el poder de CROSS APPLY para simplificar la reutilización de la lógica y proporcionar un mecanismo de "encadenamiento" para obtener resultados.

A continuación, proporcioné un Fiddle de SQL que muestra un ejemplo simple de cómo puede usar CROSS APPLY para realizar operaciones lógicas complejas en su conjunto de datos sin que las cosas se vuelvan desordenadas. No es difícil extrapolar desde aquí cálculos más complejos.

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


1

Si bien la mayoría de las consultas que emplean CROSS APPLY pueden reescribirse usando INNER JOIN, CROSS APPLY puede generar un mejor plan de ejecución y un mejor rendimiento, ya que puede limitar el conjunto que se está uniendo antes de que se produzca la unión.

Robado de aquí

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.