web-dev-qa-db-fra.com

Utilisation d'un alias externe dans une sous-requête

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

J'essaie d'afficher une liste des paiements effectués sur les transactions et d'afficher le solde actuel après chaque paiement. Voici un exemple de résultat attendu

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

et voici ma requête jusqu'à présent, mais j'ai une erreur dans la clause 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

Veuillez noter que je regroupe par p.date puisque notre préoccupation est le total des paiements effectués dans la journée.

Quelqu'un peut-il m'éclairer pourquoi je reçois cette erreur? Et existe-t-il une solution de contournement pour atteindre le résultat attendu?

10
Jaime Sangcap

Les deux sélections imbriquées dans votre requête sont appelées tables dérivées . Une table dérivée n'est pas censée être corrélée avec d'autres ensembles de données participant à la requête, par conséquent les références externes à ceux-ci dans la requête imbriquée ne sont pas autorisées.

Une façon de résoudre le problème consiste à réécrire votre requête afin de déplacer la sélection incriminée dans le contexte où la corrélation est autorisée. Dans votre cas, vous pouvez déplacer la sous-requête incriminée vers la clause 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 ici


Par souci d'exhaustivité, le standard SQL a en fait une syntaxe qui permet la corrélation pour les tables dérivées. Il est appelé jointure latérale . Du point de vue syntaxique, cela ressemble presque exactement à une jointure normale, il vous suffit d'ajouter le mot clé LATERAL aprè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
…

Le mot clé ajouté fait toute la différence, car ce n'est qu'avec ce mot clé qu'une requête imbriquée est autorisée à référencer d'autres jeux de données dans la même clause FROM (à gauche du mot clé JOIN le plus récent).

Les jointures latérales sont actuellement prises en charge par PostgreSQL et Oracle. Un concept similaire avec une syntaxe légèrement différente (et moins flexible) est également pris en charge par SQL Server. Comme vous l'avez peut-être deviné, MySQL ne supporte actuellement rien de tel.

9
McNets