2

I have two queries, one which has a condition that uc_id and ce_id are in tableUCP , and another which only requires rows which doesn't exist in the tableUCP table.

tableA : ~600k rows
tableB : ~600k rows (exactly equal to TableA)
tableCEM: ~380k rows
tableCE: ~500k rows
tableUCP: ~200k rows
tableND: ~600k rows (exactly equal to TableA)

1st query: (Runs in ~ 20 seconds) (INSERTs 0 rows)

select count(*)
FROM tableA
    JOIN tableB
        ON tableA.user_id = tableB.user_id 
        AND tableA.module = tableB.module 
    LEFT JOIN (select DISTINCT c_id,module_id from tableCEM where current = TRUE) cem
        ON tableB.module_id = cem.module_id
    LEFT JOIN (select * from tableCE where type = 'module' AND current = TRUE) ce
        ON tableA.module = ce.module 
    JOIN (select * from tableUC where current = TRUE) uc
        ON cem.c_id = uc.c_id
        AND tableB.user_id = uc.user_id 
    JOIN tableND
        ON uc.uc_id= tableND.uc_id
        AND ce.ce_id = tableND.ce_id
    JOIN (SELECT * FROM tableUCP WHERE current = TRUE) ucp
        ON uc.uc_id = ucp.uc_id
        AND ce.ce_id = ucp.ce_id;

2nd query: (Keep running for hours) (Should INSERT ~600k rows)

select count(*)
FROM tableA
    JOIN tableB
        ON tableA.user_id = tableB.user_id 
        AND tableA.module = tableB.module 
    LEFT JOIN (select DISTINCT c_id,module_id from tableCEM where current = TRUE) cem
        ON tableB.module_id = cem.module_id
    LEFT JOIN (select * from tableCE where type = 'module' AND current = TRUE) ce
        ON tableA.module = ce.module 
    JOIN (select * from tableUC where current = TRUE) uc
        ON cem.c_id = uc.c_id
        AND tableB.user_id = uc.user_id 
    JOIN tableND
        ON uc.uc_id = tableND.uc_id
        AND ce.ce_id = tableND.ce_id
    LEFT JOIN (SELECT * FROM tableUCP WHERE current = TRUE) ucp
        ON uc.uc_id = ucp.uc_id
        AND ce.ce_id = ucp.ce_id
    WHERE ucp.uc_id is NULL OR ucp.ce_id is NULL;

The only difference between the two is the last JOIN condition. Why am I getting so different results for the two queries ?

Query Plans for both the queries

I'm sorry I wouldn't be able to share any sample data, but any help or suggestions would be tremendously helpful.

I have also tried the following:

  1. Using NOT EXISTS for both columns individually.
  2. Using NOT EXISTS for both columns together.
  3. Using IN (tableA EXCEPT tableB).

All three keep running without an end.

6
  • The derived tables to get the "current" rows are not really needed. e.g. JOIN (select * from tableUC where current = TRUE) uc can be replaced with join tableuc uc on current = true and cem.c_id = uc.c_id and .... I wouldn't expect that to make a difference though. Did you try to rewrite the left join .. where .. is null? to a NOT EXISTS` condition?
    – user1822
    Commented Sep 3, 2018 at 9:37
  • Thanks for responding @a_horse_with_no_name . Yes, I have tried a NOT EXISTS condition. Let me also add the other ways I have tried to the question details.
    – Yankee
    Commented Sep 3, 2018 at 9:39
  • What count does the first query give? What count (roughly) are you expecting from the 2nd query? How many rows of data are there in each of your tables?
    – Oreo
    Commented Sep 3, 2018 at 14:27
  • @Oreo Thanks for reminding, I missed those important details, my bad. I've added it now.
    – Yankee
    Commented Sep 3, 2018 at 15:12
  • I believe that the second query does not do what you intend. If you see at the plan it filters the rows of ucp using the null conditions during ucp table scan and then performs the left join, whereas I think you want to first perform the left join and on that result remove rows using the null conditions. I think you should better use NOT EXISTS. Also I see some cast operations: tableB.user_id = (uc.user_id)::bpchar. Do all your user_id columns across tables have the same datatype?
    – dbilid
    Commented Sep 3, 2018 at 17:10

1 Answer 1

1

The OR condition in your WHERE clause is killing the performance. You only need to NULL-check one of the keys you are joining with, not both.

Also, no need to do a SELECT * FROM tableUCP when just SELECT uc_id, ce_id... would be enough.

1
  • Thanks for the answer. Unfortunately, removing the OR didn't give any performance change.
    – Yankee
    Commented Sep 3, 2018 at 10:19

Your Answer

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

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