1

My simple SELECT statement on a view in Postgres 9.3.1 is pretty slow. The receipts table has 20 million entries and the other one has around 17k.

But I still think it should be faster than ~16 seconds?

Table receipts:

     Column      |            Type           | Modifiers | Storage     | Stats target  | Description
-----------------+---------------------------+-----------+-------------+---------------+--------------
 profitcenter    | character varying(5)      | not null  | extended    |               |
 receiptnumber   | bigint                    | not null  | plain       |               |
 receiptposition | character varying(6)      | not null  | extended    |               |
 date            | date                      | not null  | plain       |               |
 customerid      | integer                   |           | plain       |               |
 vkorg           | character varying(4)      |           | extended    |               |
 artikelnummer   | integer                   |           | plain       |               |
 price           | numeric(18,2)             |           | main        |               |
Indexes receiptnumber, date and customerid as B-Tree.

Table cc_add_entry2:

    Column     |          Type          | Modifiers | Storage     | Stats target  | Description
---------------+------------------------+-----------+-------------+---------------+--------------
 receiptnumber | bigint                 | not null  | plain       |               |
 customerid    | bigint                 |           | plain       |               |
 user          | character varying(255) |           | extended    |               |
 note          | text                   |           | extended    |               |
 date          | date                   |           | plain       |               |
Indexes receiptnumber, customerid as B-Tree.

The view is simple:

 SELECT s.receiptnumber,
    s.receiptposition,
    s.date,
    COALESCE(b.customerid, (s.customerid)::bigint) AS customerid,
    s.price
   FROM (dashboard.receipts s
     LEFT JOIN dashboard.cc_add_entry2 b ON ((s.receiptnumber = b.receiptnumber)));

EXPLAIN ANALYZE over a simple query:

explain analyze select * from test2 where customerid = 520001215;

 Hash Left Join  (cost=582.40..1086787.72 rows=100185 width=37) (actual time=2124.048..15937.744 rows=52 loops=1)
   Hash Cond: (s.receiptnumber = b.receiptnumber)
   Filter: (COALESCE(b.customerid, (s.customerid)::bigint) = 520001215)
   Rows Removed by Filter: 20036943
   ->  Seq Scan on receipts s  (cost=0.00..599969.96 rows=20036996 width=29) (actual time=0.904..7187.563 rows=20036995 loops=1)
   ->  Hash  (cost=360.51..360.51 rows=17751 width=16) (actual time=5.850..5.850 rows=17751 loops=1)
         Buckets: 2048  Batches: 1  Memory Usage: 694kB
         ->  Seq Scan on cc_add_entry2 b  (cost=0.00..360.51 rows=17751 width=16) (actual time=0.005..2.537 rows=17751 l
oops=1)
 Total runtime: 15937.962 ms
1
  • The version on the server is 9.3.1
    – Beig
    Apr 3, 2015 at 9:42

2 Answers 2

1

I am pretty sure the reason is that customerid in the view is the result of:

COALESCE(b.customerid, s.customerid::bigint)

After LEFT JOIN, cast & COALESCE, the predicate cannot be pushed down, so both tables have to be read in full, before the filter customerid = 520001215 can be applied. The fact that customerid can be NULL in both tables is not helping either. Can at least one be set to NOT NULL?

Extremely inefficient.

Run this query, and compare performance:

SELECT r.receiptnumber
    ,  r.receiptposition
    ,  r.date
    ,  c.customerid
    ,  r.price
FROM   dashboard.cc_add_entry2 c 
JOIN   dashboard.receipts r USING (receiptnumber)
WHERE  c.customerid = 520001215;

UNION ALL
SELECT r.receiptnumber
    ,  r.receiptposition
    ,  r.date
    ,  r.customerid::bigint
    ,  r.price
FROM   dashboard.receipts r
LEFT   JOIN dashboard.cc_add_entry2 c USING (receiptnumber)
WHERE  r.customerid = 520001215
AND    c.customerid IS NULL;

Should be much faster. If you can't wrap it into a view, either use this query instead or write a SQL or plpgsql function that takes customerid as parameter.

Also these multicolumn indices would help some more:

CREATE INDEX foo1 ON dashboard.cc_add_entry2 (customerid, receiptnumber);
CREATE INDEX foo2 ON dashboard.cc_add_entry2 (receiptnumber)
WHERE customerid IS NULL;
CREATE INDEX foo3 ON dashboard.receipts (customerid, receiptnumber);
3
  • Thank you very much. I did manage to make a plpgsql function with your query. I do not know how to make this into a view with the same performance.
    – Beig
    Apr 3, 2015 at 9:44
  • @Beig: What's the performance now? Several orders of magnitude faster, right? Apr 3, 2015 at 9:53
  • Yep, the query takes ~7ms.
    – Beig
    Apr 3, 2015 at 11:28
-3

Do you have clustered and nonclustered indexes? Try clustered index on receipt number and date. And non clustered index on receipt number, receipt position, date, and price

1
  • Postgres does not have clustered indexes. Apr 2, 2015 at 18:13

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.