一尘不染

在sql join中使用列而不将其添加到group by子句

sql

我的实际表结构要复杂得多,但是下面是两个简化的表定义:

桌子 invoice

CREATE TABLE invoice (
  id integer NOT NULL,
  create_datetime timestamp with time zone NOT NULL,
  total numeric(22,10) NOT NULL
);

id   create_datetime   total    
----------------------------
100  2014-05-08        1000

桌子 payment_invoice

CREATE TABLE payment_invoice (
  invoice_id integer,
  amount numeric(22,10)
);

invoice_id  amount
-------------------
100         100
100         200
100         150

我想通过连接上述2个表来选择数据,并且所选数据应如下所示:-

month      total_invoice_count  outstanding_balance
05/2014    1                    550

我正在使用的查询:

select
to_char(date_trunc('month', i.create_datetime), 'MM/YYYY') as month,
count(i.id) as total_invoice_count,
(sum(i.total) - sum(pi.amount)) as outstanding_balance
from invoice i
join payment_invoice pi on i.id=pi.invoice_id
group by date_trunc('month', i.create_datetime)
order by date_trunc('month', i.create_datetime);

上面的查询给我错误的结果作为sum(i.total) - sum(pi.amount)回报(1000 + 1000 + 1000)-(100 + 200
+ 150)= 2550
我希望它返回(1000)-(100 + 200 + 150)= 550

而且我无法将其更改为i.total - sum(pi.amount),因为这样我被迫将i.total列添加到group by子句中,而我不想这样做。


阅读 167

收藏
2021-05-30

共1个答案

一尘不染

每个发票只需要一行,因此payment_invoice请先汇总-最好在加入之前。
选择整个表格后,通常最先进行汇总然后再_进行合并 的最快方法是:

SELECT to_char(date_trunc('month', i.create_datetime), 'MM/YYYY') AS month
     , count(*)                                   AS total_invoice_count
     , (sum(i.total) - COALESCE(sum(pi.paid), 0)) AS outstanding_balance
FROM   invoice i
LEFT   JOIN  (
    SELECT invoice_id AS id, sum(amount) AS paid
    FROM   payment_invoice pi
    GROUP  BY 1
    ) pi USING (id)
GROUP  BY date_trunc('month', i.create_datetime)
ORDER  BY date_trunc('month', i.create_datetime);

LEFT JOIN
在这里至关重要。您不希望丢失payment_invoice尚未在其中存在相应行的发票,而这会在Plain上发生JOIN

相应地,将其COALESCE()用于付款总和,该总和可能为NULL。

SQL Fiddle 具有改进的测试用例。

2021-05-30