我的实际表结构要复杂得多,但以下是两个简化的表定义:
表发票
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
列添加到按子句分组,并且我不想做的事。
最佳答案
您需要每张发票一行,因此请先汇总payment_invoice
- 最好在您加入之前。
选择整个表时,通常最快到 aggregate first and join later :
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
中没有对应行的发票(目前),这会发生在普通 JOIN
中。
因此,使用 COALESCE()
付款总和,可能为 NULL。
SQL Fiddle改进了测试用例。
关于sql - 在 sql join 中使用列而不将其添加到 group by 子句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23572238/