Pergunta

Qual é o principal objetivo do uso de CRUZ APLICAR?

Eu li (vagamente, através de postagens na Internet) que cross apply pode ser mais eficiente quando a seleção em grandes conjuntos de dados se está a particionar.(Paginação vem à mente)

Eu também sei que CROSS APPLY não necessita de uma UDF, como o direito da tabela.

Na maioria INNER JOIN consultas (um-para-muitos relacionamentos), eu poderia reescrever-los para usar CROSS APPLY, mas eles sempre me dão equivalente planos de execução.

Alguém pode me dar um bom exemplo de quando CROSS APPLY faz a diferença para aqueles casos em que INNER JOIN vai funcionar bem?


Editar:

Aqui está um exemplo trivial, onde os planos de execução são exatamente os mesmos.(Mostre-me onde eles diferem e onde cross apply é mais rápido, mais 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
Foi útil?

Solução

Alguém pode me dar um bom exemplo de quando o Cross Apply faz diferença nos casos em que a junção interna também funcionará?

Veja o artigo no meu blog para comparação detalhada de desempenho:

CROSS APPLY funciona melhor em coisas que não têm simples JOIN doença.

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

Não pode ser facilmente formulado com um INNER JOIN doença.

Você provavelmente poderia fazer algo assim usando CTEFunção da janela e da janela:

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

, mas isso é menos legível e provavelmente menos eficiente.

Atualizar:

Acabei de verificar.

master é uma mesa de sobre 20,000,000 registros com a PRIMARY KEY sobre 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 para quase 30 segundos, enquanto 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

é instantâneo.

Outras dicas

cross apply por vezes, permite que você faça coisas que você não pode fazer com inner join.

Exemplo (um erro de sintaxe):

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

Este é um erro de sintaxe, porque, quando usado com inner join, tabela de funções só podem tomar variáveis ou constantes como parâmetros.(I. e., a tabela de parâmetro de função não pode depender de outro da coluna da tabela.)

No entanto:

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

Isso é legal.

Editar: Ou, em alternativa, de menor sintaxe:(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+ tem Lateral Tabelas Derivadas e Postgresql (9.3+) tem Lateral Subconsultas o que pode ser usado para um efeito semelhante.

Considere que você tem duas mesas.

Tabela mestre

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

Tabela de detalhes

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

Existem muitas situações em que precisamos substituir INNER JOIN com CROSS APPLY.

1. Junte -se a duas tabelas com base em TOP n resultados

Considere se precisarmos selecionar Id e Name a partir de Master e duram duas datas para cada Id a partir 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

A consulta acima gera o seguinte 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

Veja, gerou resultados para as duas datas nas últimas duas datas Id e depois juntou -se a esses registros apenas na consulta externa em Id, o que está errado. Para conseguir isso, precisamos 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

e forma o seguinte 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

Aqui está como funciona. A consulta dentro CROSS APPLY pode fazer referência à tabela externa, onde INNER JOIN Não é possível fazer isso (lança um erro de compilação). Ao encontrar as duas últimas datas, a adesão é feita dentro CROSS APPLY ou seja, WHERE M.ID=D.ID.

2. Quando precisamos INNER JOIN funcionalidade usando funções.

CROSS APPLY pode ser usado como substituto com INNER JOIN Quando precisamos obter resultado de Master mesa e a function.

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

E aqui está a função

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

que gerou o seguinte 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

Vantagem adicional da aplicação cruzada

APPLY pode ser usado como substituto para UNPIVOT. Qualquer CROSS APPLY ou OUTER APPLY Pode ser usado aqui, que são intercambiáveis.

Considere que você tem a tabela abaixo (nomeada 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

A consulta está abaixo.

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

O que te traz o 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

Parece -me que o Cross Apply pode preencher uma certa lacuna ao trabalhar com campos calculados em consultas complexas/aninhadas e torná -las mais simples e legíveis.

Exemplo simples: você tem um DOB e deseja apresentar vários campos relacionados à idade que também dependerão de outras fontes de dados (como emprego), como idade, grupo de idade, ageatração, mínima retiração, etc. para uso em seu aplicativo de usuário final (Excel Pivottables, por exemplo).

As opções são limitadas e raramente elegantes:

  • As subconeiras de junção não podem introduzir novos valores no conjunto de dados com base nos dados na consulta dos pais (deve ficar por conta própria).

  • Os UDFs são legais, mas lentos, pois tendem a evitar operações paralelas. E ser uma entidade separada pode ser uma coisa boa (menos código) ou ruim (onde está o código).

  • Tabelas de junção. Às vezes, eles podem funcionar, mas em breve você está se juntando a subconsposições com toneladas de sindicatos. Grande bagunça.

  • Crie mais uma visualização de fins únicos, assumindo que seus cálculos não exijam dados obtidos no meio da sua consulta principal.

  • Tabelas intermediárias. Sim ... isso geralmente funciona, e geralmente uma boa opção, pois eles podem ser indexados e rápidos, mas o desempenho também pode cair devido à atualização de declarações não paralelas e a não permitir fórmulas em cascata (resultados de reutilização) para atualizar vários campos dentro dos campos dentro dos mesma declaração. E às vezes você prefere fazer as coisas de uma só vez.

  • Consultas de nidificação. Sim, a qualquer momento, você pode colocar parênteses em toda a sua consulta e usá -la como uma subconsulta na qual você pode manipular dados de origem e campos calculados. Mas você só pode fazer isso muito antes de ficar feio. Muito feio.

  • Código de repetição. Qual é o maior valor de 3 declarações de longa (caso ... mais ... fim)? Isso vai ser legível!

    • Diga aos seus clientes para calcular as malditas coisas.

Perdi alguma coisa? Provavelmente, fique à vontade para comentar. Mas ei, Cross Apply é como uma dádiva de Deus em tais situações: basta adicionar um simples CROSS APPLY (select tbl.value + 1 as someFormula) as crossTbl e voilà! Seu novo campo agora está pronto para uso praticamente como sempre esteve lá nos dados de origem.

Valores introduzidos através da Cross Apply Can ...

  • ser usado para criar um ou vários campos calculados sem adicionar problemas de desempenho, complexidade ou legibilidade à mistura
  • Como nas junções, várias declarações cruzadas subsequentes podem se referir a si mesmas: CROSS APPLY (select crossTbl.someFormula + 1 as someMoreFormula) as crossTbl2
  • Você pode usar os valores introduzidos por um cruzamento aplicado nas condições de junção subsequente
  • Como bônus, há o aspecto da função com valor de mesa

Droga, não há nada que eles não possam fazer!

Cross Apply funciona bem com um campo XML também. Se você deseja selecionar valores de nó em combinação com outros campos.

Por exemplo, se você tiver uma tabela contendo algum XML

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

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

Retornará um resultado

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

Isso já foi respondido muito bem tecnicamente, mas deixe -me dar um exemplo concreto de como é extremamente útil:

Digamos que você tenha duas tabelas, cliente e pedido. Os clientes têm muitos pedidos.

Quero criar uma visão que me dê detalhes sobre os clientes e o pedido mais recente que eles fizeram. Com apenas junções, isso exigiria alguns joios e agregação que não são bonitos. Mas com a aplicação cruzada, é super fácil:

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

Cross Apply pode ser usado para substituir a subconstração, onde você precisa de uma coluna da subconsulta

subconsiva

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

Aqui não poderei selecionar as colunas da tabela da empresa, por isso, usando o Cross Apply

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

Eu acho que deveria ser legibilidade;)

Cross Applar será um pouco exclusivo para as pessoas que lêem para dizer que um UDF está sendo usado que será aplicado a cada linha da tabela à esquerda.

É claro que existem outras limitações em que uma aplicação cruzada é melhor usada do que a participação nos quais outros amigos postaram acima.

Aqui está um artigo que explica tudo, com sua diferença de desempenho e uso sobre junções.

SQL Server Cross Aplicar e Aplicar externo em junções

Conforme sugerido neste artigo, não há diferença de desempenho entre eles para operações normais de junção (interno e cruz).

enter image description here

A diferença de uso chega quando você precisa fazer uma 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)

Ou seja, quando você precisa se relacionar com a função. Isso não pode ser feito usando a junção interna, o que lhe daria o erro "O identificador de várias partes" D.DepartmentId "não poderia estar vinculado". Aqui, o valor é passado para a função, pois cada linha é lida. Parece legal para mim. :)

Bem, não tenho certeza se isso se qualifica como um motivo para usar o Cross Apply versus o interno junção, mas essa consulta foi respondida para mim em um post do fórum usando Cross Apply, por isso não tenho certeza se existe um método igualdivente usando a junção interna:

Create PROCEDURE [dbo].[Message_FindHighestMatches]

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

Como começar

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

FIM

A essência do operador de aplicação é permitir a correlação entre o lado esquerdo e direito do operador na cláusula From.

Em contraste com a participação, a correlação entre entradas não é permitida.

Falando sobre correlação no operador Apply, quero dizer no lado direito que podemos colocar:

  • uma mesa derivada - como uma subconsência correlacionada com um pseudônimo
  • Uma função de tabela valorizada - uma visão conceitual com parâmetros, onde o parâmetro pode se referir ao lado esquerdo

Ambos podem retornar várias colunas e linhas.

Talvez essa seja uma pergunta antiga, mas ainda amo o poder da Cross se aplicar para simplificar a reutilização da lógica e fornecer um mecanismo de "encadeamento" para obter resultados.

Eu forneci um violino SQL abaixo, que mostra um exemplo simples de como você pode usar o Cross Apply para executar operações lógicas complexas em seu conjunto de dados sem que as coisas fiquem confusas. Não é difícil extrapolar daqui cálculos mais complexos.

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top