Pregunta

¿Cuál es el objetivo principal del uso? APLICACIÓN CRUZADA?

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

Yo también sé que CROSS APPLY no requiere una UDF como tabla derecha.

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

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


Editar:

Aquí hay un ejemplo trivial, donde los planes de ejecución son exactamente los mismos.(Muéstrame uno en qué se diferencian y dónde cross apply es 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
¿Fue útil?

Solución

¿Alguien puede darme un buen ejemplo de cuándo la aplicación cruzada marca la diferencia en aquellos casos en los que la unión interna también funcionará?

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

CROSS APPLY funciona mejor en cosas que no tienen simples JOIN condición.

Este selecciona 3 Últimos registros de t2 Para cada 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 un INNER JOIN condición.

Probablemente podrías hacer algo así usando CTE'S y función de la 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.

master es una tabla de aproximadamente 20,000,000 Registros con un PRIMARY KEY en 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 30 segundos, mientras 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.

Otros consejos

cross apply a 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

Esto es un error de sintaxis, porque cuando se usa con inner join, las funciones de la 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+ ha Tablas derivadas laterales y PostgreSQL (9.3+) tiene Subcontrol laterales que puede usarse con un efecto similar.

Considera que tienes dos tablas.

Mesa maestra

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 necesitamos reemplazar INNER JOIN con CROSS APPLY.

1. Une dos tablas basadas en TOP n resultados

Considere si necesitamos seleccionar Id y Name de Master y las dos últimas fechas para cada Id de 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

Ver, generó resultados para las últimas dos fechas con las últimas dos fecha Id y luego se unió a estos registros solo en la consulta externa en Id, Cuál está mal. 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 dentro CROSS APPLY puede hacer referencia a la tabla exterior, donde INNER JOIN No se 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 JOIN funcionalidad utilizando funciones.

CROSS APPLY se puede usar como reemplazo con INNER JOIN Cuando necesitamos obtener el resultado de Master mesa y un 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 aplicar cruzado

APPLY se puede usar como reemplazo para UNPIVOT. O CROSS APPLY o OUTER APPLY se puede usar aquí, que son intercambiables.

Considere que tiene la tabla a continuación (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á debajo.

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

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

Ejemplo sencillo:tiene una fecha de nacimiento y desea presentar varios campos relacionados con la edad que también dependerán de otras fuentes de datos (como el empleo), como Edad, Grupo de edad, Edad en la contratación, Fecha mínima de jubilación, etc.para usar en su aplicación de usuario final (Excel PivotTables, 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).

  • Las UDF son claras, pero lentas, ya que tienden a impedir 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 unirás a subconsultas con toneladas de UNION.Gran desorden.

  • Cree otra vista de propósito único, suponiendo que sus cálculos no requieran 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 UPDATE no son paralelas y no permiten aplicar fórmulas en cascada (reutilizar resultados) para actualizar varios campos dentro de la misma declaración.Y a veces preferirías hacer las cosas de una sola vez.

  • Consultas anidadas.Sí, en cualquier momento puede poner paréntesis en toda su consulta y usarla como una subconsulta sobre la cual puede manipular los datos de origen y los campos calculados por igual.Pero sólo puedes hacer esto hasta cierto punto antes de que se ponga feo.Muy feo.

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

    • Dígales a sus clientes que calculen las malditas cosas ellos mismos.

¿Me he perdido algo?Probablemente, así que no dudes en comentar.Pero bueno, CROSS APPLY es como una bendición en tales situaciones:solo agregas un simple CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl ¡y voilá!Su nuevo campo ahora está listo para usar prácticamente como siempre estuvo allí en sus datos de origen.

Los valores introducidos mediante CROSS APPLY pueden...

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

¡Maldita sea, no hay nada que no puedan hacer!

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

Por ejemplo, si tiene una tabla que contiene un poco de 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

Esto ya ha sido 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 orden más reciente que han realizado. Con solo uniones, esto requeriría algunas auto-uniones y agregación que no es bonita. Pero con la aplicación cruzada, 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

Se puede utilizar la aplicación cruzada para reemplazar los subconsules donde necesita una columna de la subconsulta

subconsulto

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 empresa, por lo que el uso de 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

Supongo que debería ser legibilidad;)

La aplicación cruzada será algo única para las personas que leen para decirles que se está utilizando un UDF que se aplicará a cada fila desde la tabla de la izquierda.

Por supuesto, hay otras limitaciones en las que se aplica una cruz se usa mejor que unirse a los que otros amigos han publicado anteriormente.

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

SQL Server Cross Aplic y Outer Aplica sobre uniones

Como se sugiere en este artículo, no existe una diferencia de rendimiento entre ellos para las operaciones de unión normales (interior y cruzado).

enter image description here

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 usando la unión interna, lo que le daría el error "El identificador de varias partes" D.DePartmentId "no pudo estar atado". Aquí el valor se pasa a la función a medida que se lee cada fila. Me suena genial. :)

Bueno, no estoy seguro de si esto califica como una razón para usar la aplicación cruzada versus la unión interna, pero esta consulta fue respondida para mí en una publicación del foro usando la aplicación cruzada, por lo que no estoy seguro de si hay un método igualivente usando unión interna:

Create PROCEDURE [dbo].[Message_FindHighestMatches]

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

A medida que 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

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

A diferencia de unirse, no se permite la correlación entre las entradas.

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

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

Ambos pueden devolver varias columnas y filas.

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

He proporcionado un violín SQL a continuación que muestra un ejemplo simple de cómo puede usar la aplicación cruzada 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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top