Menggunakan alias luar dalam subquery

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

Saya mencoba menampilkan daftar pembayaran yang dilakukan pada transaksi dan menunjukkan saldo saat ini setelah setiap pembayaran. Di bawah ini adalah contoh dari hasil yang diharapkan

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

dan di sini adalah permintaan saya sejauh ini, tetapi memiliki kesalahan di mana klausa

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

Harap perhatikan bahwa saya dikelompokkan p.datekarena kekhawatiran kami adalah total pembayaran yang dilakukan dalam sehari.

Dapatkah seseorang tolong beri tahu saya mengapa saya mendapatkan kesalahan itu? Dan apakah ada solusi untuk mencapai hasil yang diharapkan?

Jaime Sangcap
sumber

Jawaban:

10

Dua pilihan bersarang dalam kueri Anda disebut tabel turunan . Tabel turunan tidak dimaksudkan untuk dikorelasikan dengan kumpulan data lain yang berpartisipasi dalam kueri, oleh karena itu referensi luar kepada mereka dalam kueri bersarang tidak diperbolehkan.

Salah satu cara untuk mengatasi masalah ini adalah dengan menulis ulang kueri Anda untuk memindahkan pilih yang menyinggung ke konteks di mana korelasi diperbolehkan. Dalam kasus Anda, Anda dapat memindahkan subquery yang menyinggung ke klausa 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 di sini


Demi kelengkapan, standar SQL sebenarnya memiliki sintaks yang memungkinkan korelasi untuk tabel turunan. Ini disebut lateral join . Dari sudut pandang sintaksis, tampilannya hampir persis seperti gabungan normal, Anda hanya perlu menambahkan LATERALkata kunci setelah 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

Kata kunci yang ditambahkan membuat semua perbedaan, karena hanya dengan kata kunci itu sebuah query bersarang diizinkan untuk mereferensikan dataset lain dalam klausa FROM yang sama (di sebelah kiri kata kunci JOIN terbaru).

Gabungan lateral saat ini didukung oleh PostgreSQL dan Oracle. Konsep serupa dengan sintaks yang sedikit berbeda (dan kurang fleksibel) juga didukung oleh SQL Server. Seperti yang sudah Anda duga, MySQL saat ini tidak mendukung hal semacam itu.

McNets
sumber
MariaDB mendukung fungsi jendela yang dapat berguna untuk menjalankan masalah total seperti ini: mariadb.com/kb/en/library/window-functions
ypercubeᵀᴹ
Mainstream MySQL akan memiliki fungsi jendela dalam versi 8: dev.mysql.com/doc/refman/8.0/en/window-functions.html Dugaan saya untuk kapan tahun ini, mungkin dalam 6 bulan pertama (mengingat di atas disebutkan: "Draf Ketersediaan Pra-Umum: 2018-01-12").
ypercubeᵀᴹ
@ McNets dan Andriy saya membuatnya bekerja sekarang menggunakan jawaban Anda. Anda menjelaskannya dengan baik dan dengan beberapa takeaways (kata kunci lateral). Terima kasih!
Jaime Sangcap
Saya senang membantu.
McNets
@JaimeSangcap: Senang membantu, bersorak.
Andriy M