2

I have an aggregate query with about 4 subqueries. The issue is, with only 2500+ rows, it takes about 5~ sec to complete. Is there a way any of you can help optimize this query?

Any help will be much appreciated.

SELECT * FROM (
SELECT
    DISTINCT ON(i.name) i.name,
    i.invoice_number,
    i.created_date,
    (SELECT COUNT (i2.customer_id) FROM invoices i2 WHERE i.invoice_number = i2.invoice_number) AS total_customers,
    (SELECT COUNT (i2.status) FROM invoices i2 WHERE i.invoice_number = i2.invoice_number AND i2.status = 'PAID') AS total_full_payments,
    (SELECT COUNT (i2.status) FROM invoices i2 WHERE i.invoice_number = i2.invoice_number AND i2.status = 'PARTIALLY_PAID') AS total_part_payments,
    (SELECT COUNT (i2.status) FROM invoices i2 WHERE i.invoice_number = i2.invoice_number AND i2.status = 'UNPAID') AS total_no_payments
FROM
    invoices i WHERE i.client_id = 2
GROUP BY
    i.name,
    i.customer_id,
    i.invoice_number,
    i.created_date
) i
ORDER BY i.created_date DESC
OFFSET 0 LIMIT 10
1
  • Please provide sample data, desired results, and an explanation of what the code is supposed to be doing. Commented Aug 22, 2020 at 12:47

1 Answer 1

4

Use conditional aggregation! It is rather unclear what your query is doing, but this seems like a reasonable query:

SELECT i.name, MIN(i.created_date),
       COUNT(*) as total_customers,
       COUNT(*) FILTER (WHERE i.status = 'PAID') AS total_full_payments,
       COUNT(*) FILTER (WHERE i.status = 'PARTIALLY_PAID') AS total_part_payments,
       COUNT(*) FILTER (WHERE i.status = 'UNPAID') AS total_no_payments
FROM invoices i
WHERE i.client_id = 2
GROUP BY i.name
ORDER BY MIN(created_date) DESC;
Sign up to request clarification or add additional context in comments.

3 Comments

is the FILTER keyword specific to postgresql? This seems similar to SUM(CASE WHEN i.status = 'PAID' THEN 1 ELSE 0) but I like this syntax better. +1
@Cole . . . It is standard SQL. However, Postgres is one of the few databases that support it. In lieu of the SUM(CASE), though, I would suggest SUM( (i.status = 'PAID')::INT ).
Tested and it's very performant. Works pretty well. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.