views:

47

answers:

1

I need a SQL query that returns results matched by EITHER of the following SQL queries:

Query 1:

SELECT "annotations".* FROM "annotations" INNER JOIN "votes" ON "votes".voteable_id = "annotations".id AND "votes".voteable_type = 'Annotation'
WHERE (votes.vote = 't' AND votes.voter_id = 78)

Query 2:

SELECT "annotations".* FROM "annotations" INNER JOIN "songs" ON "songs".id = "annotations".song_id INNER JOIN "songs" songs_annotations ON "songs_annotations".id = "annotations".song_id INNER JOIN "users" ON "users".id = "songs_annotations".state_last_updated_by_id
WHERE (annotations.referent IS NOT NULL AND annotations.updated_at < '2010-04-05 01:51:24' AND (body = '?' OR body LIKE '%[?]%') AND ((users.id = songs.state_last_updated_by_id and users.needs_edit = 'f' and songs.state != 'work_in_progress') OR (songs.state = 'published'))

Here's what I tried, but it doesn't work:

SELECT "annotations".* FROM "annotations" INNER JOIN "songs" ON "songs".id = "annotations".song_id INNER JOIN "songs" songs_annotations ON  "songs_annotations".id = "annotations".song_id INNER JOIN "users" ON "users".id = "songs_annotations".state_last_updated_by_id  INNER JOIN "votes" ON "votes".voteable_id = "annotations".id AND "votes".voteable_type = 'Annotation' WHERE ((votes.vote = 't' and votes.voter_id = 78) OR (annotations.referent IS NOT NULL and annotations.updated_at < '2010-04-05 01:43:52' and (annotations.body = '?' OR annotations.body LIKE '%[?]%') and ((users.id = songs.state_last_updated_by_id and users.needs_edit = 'f') OR songs.state = 'published')))
+1  A: 

UNION is the simplest. If your engine's optimizer doesn't do an efficient job with that, I might look deeper (but anything else would probably involve a lot of LEFT JOINs, since it looks you are joining two distinct uses of annotations):

SELECT "annotations".*
FROM "annotations"
INNER JOIN "votes"
    ON "votes".voteable_id = "annotations".id 
    AND "votes".voteable_type = 'Annotation'
WHERE (votes.vote = 't' AND votes.voter_id = 78)
UNION
SELECT "annotations".*
FROM "annotations"
INNER JOIN "songs"
     ON "songs".id = "annotations".song_id
INNER JOIN "songs" songs_annotations
     ON "songs_annotations".id = "annotations".song_id
INNER JOIN "users"
     ON "users".id = "songs_annotations".state_last_updated_by_id
WHERE (annotations.referent IS NOT NULL 
     AND annotations.updated_at < '2010-04-05 01:51:24' 
     AND (body = '?' OR body LIKE '%[?]%')
     AND ((users.id = songs.state_last_updated_by_id and users.needs_edit = 'f' and songs.state != 'work_in_progress') OR (songs.state = 'published'))

For the same reason your INNER JOIN attempt failed (all join criteria must be satisfied), you would have to change almost everything to LEFT JOIN (or LEFT JOIN to a nested INNER JOIN) - I think the UNION is simplest.

SELECT "annotations".*
FROM "annotations"
LEFT JOIN "votes"
    ON "votes".voteable_id = "annotations".id 
    AND "votes".voteable_type = 'Annotation'
LEFT JOIN "songs"
     ON "songs".id = "annotations".song_id
LEFT JOIN "songs" songs_annotations
     ON "songs_annotations".id = "annotations".song_id
LEFT JOIN "users"
     ON "users".id = "songs_annotations".state_last_updated_by_id
WHERE (votes.vote = 't' AND votes.voter_id = 78)
     OR
     (annotations.referent IS NOT NULL 
     AND annotations.updated_at < '2010-04-05 01:51:24' 
     AND (body = '?' OR body LIKE '%[?]%')
     AND ((users.id = songs.state_last_updated_by_id and users.needs_edit = 'f' and songs.state != 'work_in_progress') OR (songs.state = 'published'))
Cade Roux
This is a good approach -- unfortunately I'm trying to put this in a `named_scope` in Rails, and `named_scopes`s do not support `UNION`
Horace Loeb
@Horace Loeb - check out the all LEFT JOIN solution. The optimizer typically will not handle this well - not only are the LEFT JOINs hindering re-ordering filter operations, the OR clause in the WHERE also makes a lot more scans likely.
Cade Roux
@Horace Loeb - and of course, the all LEFT JOIN solution may(is likely to) be semantically incorrect for your model...
Cade Roux
Caveats aside, it looks like this works! You're a genius, Cade! (The query is for http://rapgenius.com, which you can check out if you like rap music)
Horace Loeb