Search code examples
sqljoinselectinner-query

Improve SQL query by replacing inner query


I'm trying to simplify this SQL query (I replaced real table names with metaphorical), primarily get rid of the inner query, but I'm brain frozen can't think of any way to do it.

My major concern (aside from aesthetics) is performance under heavy loads

The purpose of the query is to count all books grouping by genre found on any particular shelve where the book is kept (hence the inner query which is effectively telling which shelve to count books on).

SELECT g.name, count(s.book_id) occurances FROM genre g
LEFT JOIN shelve s ON g.shelve_id=s.id
WHERE s.id=(SELECT genre_id FROM book WHERE id=111)
GROUP BY s.genre_id, g.name

Solution

  • It seems like you want to know many books that are on a shelf are in the same genre as book 111: if you liked book "X", we have this many similar books in stock.

    One thing I noticed is the WHERE clause in the original required a value for the shelve table, effectively converting it to an INNER JOIN. And speaking of JOINs, you can JOIN instead of the nested select.

    SELECT g.name, count(s.book_id) occurances 
    FROM genre g
    INNER JOIN shelve s ON s.id = b.shelve_id
    INNER JOIN book b on b.genre_id = s.id 
    WHERE b.id=111
    GROUP BY g.id, g.name
    

    Thinking about it more, I might also start with book rather than genre. In the end, the only reason you need the genre table at all is to find the name, and therefore matching to it by id may be more effective.

    SELECT g.name, count(s.book_id) occurances 
    FROM book b 
    INNER JOIN shelve s ON s.id = b.genre_id
    INNER JOIN genre g on g.shelve_id = s.id
    WHERE b.id=111
    GROUP BY g.id, g.name
    

    Not sure they meet your idea of "simpler" or not, but they are alternatives.

    ... unless matching shelve.id with book.genre_id is a typo in the question. It seems very odd the two tables would share the same id values, in which case these will both be wrong.