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