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

11
|    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 suis en train de regrouper, p.datecar 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?

Jaime Sangcap
la source

Réponses:

10

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. On l'appelle jointure latérale . Du point de vue syntaxique, cela ressemble presque exactement à une jointure normale, il vous suffit d'ajouter le LATERALmot - clé 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 ensembles 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.

McNets
la source
MariaDB prend en charge les fonctions de fenêtre qui peuvent être utiles pour exécuter des problèmes de totaux comme celui-ci: mariadb.com/kb/en/library/window-functions
ypercubeᵀᴹ
Mainstream MySQL aura une fonction de fenêtre dans la version 8: dev.mysql.com/doc/refman/8.0/en/window-functions.html Je suppose que c'est quand cette année, probablement dans les 6 premiers mois (étant donné que ce qui précède dit: "Avant-projet de disponibilité générale: 2018-01-12").
ypercubeᵀᴹ
@McNets et Andriy Je l'ai fait fonctionner maintenant en utilisant votre réponse. Vous l'avez bien expliqué et avec quelques plats à emporter (mot-clé latéral). Je vous remercie!
Jaime Sangcap
Je suis ravi de vous aider.
McNets
@JaimeSangcap: Heureux d'aider, cheers.
Andriy M