在子查询中使用外部别名


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因为我们关注的是一天之内的总付款额。

有人可以启发我为什么会出错吗?是否有任何变通方法可以达到预期的结果?

Answers:


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

add关键字起到了所有作用,因为只有该关键字才允许嵌套查询引用同一FROM子句中的其他数据集(在最新的JOIN关键字的左侧)。

PostgreSQL和Oracle当前支持横向连接。SQL Server还支持语法稍有不同(且灵活性较差)的类似概念。您可能已经猜到了,MySQL目前不支持任何此类功能。


MariaDB的支持窗口类函数可用于运行这样一个总计的问题是有用:mariadb.com/kb/en/library/window-functions
ypercubeᵀᴹ

主流MySQL在版本8中将具有窗口功能:dev.mysql.com/doc/refman/8.0/en/window-functions.html我的猜测是今年什么时候,大概在头6个月内(考虑到上面的说法: “预发布可用性草案:2018年1月12日”)。
ypercubeᵀᴹ

@McNets和Andriy现在可以使用您的答案进行工作了。您已经很好地解释了它,并附带了一些要点(横向关键字)。谢谢!
Jaime Sangcap

我很乐意提供帮助。
McNets

@JaimeSangcap:乐意帮助,欢呼。
Andriy M,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.