Usando alias externo em uma subconsulta

11
|    payments    |  | transactions |  | transaction_items |
|:--------------:|  |:------------:|  |:-----------------:|
|       id       |  |      id      |  |         id        |
|      date      |  |    number    |  |   transaction_id  |
|     amount     |  |     date     |  |    description    |
| transaction_id |  |      val     |  |       price       |
                                      |      discount     |
                                      |      quantity     |

Estou tentando exibir uma lista de pagamentos feitos nas transações e mostrar o saldo atual após cada pagamento. Abaixo está um exemplo de resultado esperado

| number | DATE(p.date) | total   | paid    | balance | 
| 1355   | 2016-10-31   | 899.00  | 450.00  | 449.00  | 
| 1355   | 2016-12-06   | 899.00  | 449.00  | 0.00    | 
| 1359   | 2016-09-28   | 4045.00 | 1515.00 | 2530    | 
| 1359   | 2016-10-24   | 4045.00 | 35.00   | 2495.00 | 
| 1361   | 2016-09-28   | 1548.00 | 1548.00 | 0.00    | 

e aqui está minha consulta até agora, mas há um erro na cláusula where

select
    t.number,
    DATE(p.date),
    ti.total 'total',
    SUM(p.amount) 'paid',
    ti.total - paid.total 'balance'
from payments p
left join transactions t
on p.transaction_id = t.id
left join (
    select inner_ti.transaction_id, sum((inner_ti.price - inner_ti.discount) * inner_ti.quantity)  'total'
    from transaction_items inner_ti
    group by inner_ti.transaction_id
) ti on t.id = ti.transaction_id
left join (
    select inner_p.transaction_id, sum(inner_p.amount) 'total'
    from payments inner_p
    where inner_p.date <= p.date -- error unknown column p.date
    group by inner_p.transaction_id
) paid on t.id = paid.transaction_id
group by t.number, DATE(p.date), ti.total, paid.total
order by DATE(p.date) ASC

Observe que estou agrupando, p.datepois nossa preocupação é com o total de pagamentos efetuados dentro do dia.

Alguém pode me esclarecer por que estou recebendo esse erro? E existe alguma solução alternativa para alcançar o resultado esperado?

Jaime Sangcap
fonte

Respostas:

10

As duas seleções aninhadas em sua consulta são chamadas de tabelas derivadas . Uma tabela derivada não deve ser correlacionada com outros conjuntos de dados que participam da consulta, portanto, referências externas a eles na consulta aninhada não são permitidas.

Uma maneira de resolver o problema é reescrever sua consulta para mover a seleção incorreta para o contexto em que a correlação é permitida. No seu caso, você pode mover a subconsulta incorreta para a cláusula SELECT:

select    t.number,
          DATE(p.date),
          ti.total 'total',
          SUM(p.amount) 'paid',
          ti.total - (select sum(inner_p.amount)
                      from     payments inner_p
                      where    inner_p.transaction_id = p.transaction_id
                      and      inner_p.date <= p.date
                     ) 'balance'
from      payments p
left join transactions t
on        p.transaction_id = t.id
left join (
          select   inner_ti.transaction_id, 
                   sum((inner_ti.price - inner_ti.discount) * inner_ti.quantity)  'total'
          from     transaction_items inner_ti
          group by inner_ti.transaction_id
          ) ti 
on        t.id = ti.transaction_id
group by  t.number, DATE(p.date), ti.total, 'balance'
order by  DATE(p.date) ASC;

rextester aqui


Por uma questão de integridade, o padrão SQL realmente possui uma sintaxe que permite a correlação para tabelas derivadas. É chamado junção lateral . Do ponto de vista sintático, parece quase exatamente como uma junção normal, você só precisa adicionar a LATERALpalavra - chave após JOIN:


left join lateral (
    select inner_p.transaction_id, sum(inner_p.amount) 'total'
    from payments inner_p
    where inner_p.date <= p.date -- this outer reference would be valid
    group by inner_p.transaction_id
) paid on t.id = paid.transaction_id

A palavra-chave adicionada faz toda a diferença, pois somente com essa palavra-chave é permitida uma consulta aninhada para referenciar outros conjuntos de dados na mesma cláusula FROM (à esquerda da palavra-chave JOIN mais recente).

As junções laterais são atualmente suportadas pelo PostgreSQL e Oracle. Um conceito semelhante com uma sintaxe ligeiramente diferente (e menos flexível) também é suportado pelo SQL Server. Como você deve ter adivinhado, o MySQL atualmente não suporta nada desse tipo.

McNets
fonte
MariaDB suporta fucntions janela que pode ser útil para a execução de problemas totais como este: mariadb.com/kb/en/library/window-functions
ypercubeᵀᴹ
O MySQL mainstream terá a função window na versão 8: dev.mysql.com/doc/refman/8.0/en/window-functions.html Meu palpite para quando é este ano, provavelmente nos primeiros 6 meses (considerando que o exposto acima diz: "Rascunho de disponibilidade pré-geral: 12-01-2018").
usar o seguinte comando
@ McNets e Andriy Eu consegui trabalhar agora usando sua resposta. Você explicou bem e com algumas sugestões (palavra-chave lateral). Obrigado!
Jaime Sangcap
Fico feliz em ajudar.
McNets
@JaimeSangcap: Feliz em ajudar, felicidades.
Andriy M