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

I'm trying to display a list of payments made on transactions and show the current balance after each payment. Below is an example of expected result

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

and here is my query so far, but have an error in the where clause

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

Please note that I'm grouping by p.date since our concern is total payments made within the day.

Can someone please enlighten me why I'm getting that error? And is there any workaround to achieve the expected result?

12

The two nested selects in your query are called derived tables. A derived table is not meant to be correlated with other datasets participating in the query, hence outer references to them in the nested query are not allowed.

One way to resolve the issue is to rewrite your query so as to move the offending select to the context where correlation is allowed. In your case you can move the offending subquery to the SELECT clause:

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 here


For the sake of completeness, the SQL standard actually has syntax that allows correlation for derived tables. It is called lateral join. From the syntactical point of view, it looks almost exactly like a normal join, you just need to add the LATERAL keyword after 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
…

The added keyword makes all the difference, as only with that keyword a nested query is permitted to reference other datasets in the same FROM clause (to the left of the most recent JOIN keyword).

Lateral joins are currently supported by PostgreSQL and Oracle. A similar concept with a slightly different (and less flexible) syntax is also supported by SQL Server. As you may have guessed, MySQL does not currently support anything of the sort.

5
  • MariaDB supports window fucntions which can be handy for running totals problems like this one: mariadb.com/kb/en/library/window-functions Jan 21 '18 at 14:10
  • Mainstream MySQL will have window function in version 8: dev.mysql.com/doc/refman/8.0/en/window-functions.html My guess for when is this year, probably in the first 6 months (considering that the above says: "Pre-General Availability Draft: 2018-01-12"). Jan 21 '18 at 14:13
  • @McNets and Andriy I got it working now using your answer. You explained it well and with some takeaways (lateral keyword). Thank you! Jan 22 '18 at 6:07
  • I'm glad to help.
    – McNets
    Jan 22 '18 at 14:48
  • @JaimeSangcap: Happy to help, cheers.
    – Andriy M
    Jan 22 '18 at 15:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.