1

I'm having the trouble with the following query due to the growing size of the database ( websites is around a million rows, misspelled goes up to a couple millions )

It can reach up to 165 seconds in run time, I was wondering if there was a way to improve this without having to add additional columns to the tables, maybe using some king of index ?

SELECT contacturl, url 
FROM websites 
WHERE contactable = true
  AND websites.contacted = false 
  AND url IN (
    SELECT url
    FROM scans
    WHERE scanid IN (
      SELECT distinct(scanid)
      FROM misspelled
      WHERE word = ?
    )
  )
;

Edit : As requested, the output of the explain analyze :

enter image description here

Edit (1) : After Checking the explain statement output, this sing select seems to be taking most of the time :

select distinct(scanid) from misspelled where word = ?
4

2 Answers 2

1

Try going with an EXISTS test. First, let's be explicit about where the columns are coming from, also you don't need col = TRUE and col = FALSE on bools. You can simply say WHERE col or WHERE NOT col, I think that's a bit cleaner.

SELECT w.contacturl, w.url 
FROM websites AS w
WHERE w.contactable
  AND NOT w.contacted
  AND w.url IN (
    SELECT s.url
    FROM scans AS s
    WHERE s.scanid IN (
      SELECT distinct(m.scanid)
      FROM misspelled AS m
      WHERE m.word = ?
    )
  )
;

Now, with EXISTS,

SELECT w.contacturl, w.url 
FROM websites AS w
WHERE w.contactable
  AND NOT w.contacted 
  AND EXISTS (
    SELECT 1
    FROM scans AS s
    WHERE s.scanid IN (
      SELECT distinct(m.scanid)
      FROM misspelled AS m
      WHERE m.word = ?
    )
    AND w.url = s.url
  )
;

But you can do even better. DISTINCT just reduces duplicates for IN. With EXISTS duplicates are simply ignored.

SELECT w.contacturl, w.url 
FROM websites AS w
WHERE w.contactable
  AND NOT w.contacted 
  AND EXISTS (
    SELECT 1
    FROM scans AS s
    WHERE w.url = s.url
    AND EXISTS (
      SELECT 1
      FROM misspelled AS m
      WHERE m.scanid = s.scanid
      AND m.word = ?
    )
  )
;

That should be a lot faster, normally.

0

Replacing the IN with JOIN might do the trick.

WITH s1 AS
  (SELECT DISTINCT scanid
   FROM misspelled
   WHERE word = ?),
     s2 AS
  (SELECT url
   FROM scans
   JOIN s1 USING (scanid))
SELECT contacturl,
       url
FROM websites
JOIN s2 USING (url)
WHERE contactable = TRUE
  AND websites.contacted = FALSE;

Probably need many indexes:

  • word ON mispelled
  • url ON websites
  • contactable AND contacted ON websites

AND without CTEs (although I am not sure if it will improve performance):

SELECT contacturl,
       url
FROM websites
JOIN
  (SELECT url
   FROM scans
   JOIN
     (SELECT DISTINCT scanid
      FROM misspelled
      WHERE word = ?) s1 USING (scanid)) s2 USING (url)
WHERE contactable = TRUE
  AND websites.contacted = FALSE;
5
  • 2
    Use of a CTE here is likely to be extremely bad for performance. Use subqueries in FROM instead. PostgreSQL's CTEs are (unfortunately) optimisation fences. Commented Oct 14, 2015 at 23:00
  • Thanks, Performance got a little bit better, from 160 seconds to 60 seconds I think but it's still not enough. Commented Oct 20, 2015 at 8:50
  • 1
    I am sorry but 160-> 60s is not a little better. It is 3 times faster=300% improvement which is impressive. Realize that it cannot be improved by SQL rewrite. You do a JOIN on text values (url). Could not you use urlID?
    – Alexandros
    Commented Oct 20, 2015 at 9:17
  • Oh I know, I did not mean in anyway to under-mind your solution, an x3 improvement is quite something. But it happens that we have a dozen of these queries which are supposed load on a webpage. so you can understand which it's still not enough for this scenario. I'll have to go over the DB Schema and see if I can do something. Commented Oct 20, 2015 at 18:20
  • Actually, I re ran the tests with the same conditions ( I have a memory leak on my PC ) and I got the same results for both queries : Before : explain.depesz.com/s/EGBT After : explain.depesz.com/s/wFa4 I also tried adding a urlid, same thing Commented Oct 21, 2015 at 21:58

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.