Использование внешнего псевдонима в подзапросе


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

Я пытаюсь отобразить список платежей по сделкам и показывать текущий баланс после каждого платежа. Ниже приведен пример ожидаемого результата

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

и вот мой запрос, но есть ошибка в предложении 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

Пожалуйста, обратите внимание, что я группирую, p.dateтак как наша забота - это общая сумма платежей за день.

Может кто-нибудь объяснить мне, почему я получаю эту ошибку? И есть ли обходной путь для достижения ожидаемого результата?

Ответы:


10

Два вложенных выбора в вашем запросе называются производными таблицами . Производная таблица не предназначена для корреляции с другими наборами данных, участвующими в запросе, поэтому внешние ссылки на них во вложенном запросе не допускаются.

Один из способов решения этой проблемы - переписать ваш запрос, чтобы переместить ошибочный выбор в контекст, где разрешена корреляция. В вашем случае вы можете переместить ошибочный подзапрос в предложение 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;

здесь тестер


Ради полноты, стандарт SQL на самом деле имеет синтаксис, который позволяет корреляцию для производных таблиц. Это называется боковым соединением . С синтаксической точки зрения это выглядит почти как обычное соединение, вам просто нужно добавить LATERALключевое слово после 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

Ключевое слово добавленное имеет все значение, так как только с этим ключевым словом вложенный запрос может ссылаться на другие наборы данных в том же предложении FROM (слева от самого последнего ключевого слова JOIN).

Боковые объединения в настоящее время поддерживаются PostgreSQL и Oracle. Аналогичная концепция с немного другим (и менее гибким) синтаксисом также поддерживается SQL Server. Как вы уже догадались, MySQL в настоящее время не поддерживает ничего подобного.


MariaDB поддерживает оконные функции, которые могут быть полезны для выполнения задач по итоговым
ypercubeᵀᴹ

Основная версия MySQL будет иметь оконную функцию в версии 8: dev.mysql.com/doc/refman/8.0/en/window-functions.html Я предполагаю, когда наступит этот год, вероятно, в первые 6 месяцев (учитывая, что в приведенном выше тексте сказано: «Предварительная общая готовность проекта: 2018-01-12»).
ypercubeᵀᴹ

@McNets and Andriy Теперь я работаю, используя ваш ответ. Вы объяснили это хорошо и с некоторыми выносами (боковое ключевое слово). Спасибо!
Хайме Сангкап

Я рад помочь.
Макнетс

@JaimeSangcap: Рад помочь, ура.
Андрей М
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.