My approach is a bit more generic, putting the filter parameters in tables and then using GROUP BY, HAVING and COUNT to filter the results. I've used this basic approach several times for some very sophisticated 'searching' and it works very well (for me grin).
I also don't join on the Artist and Venue dimension tables initially. I'd get the results as id's (just needing artist_tag and venue_tag) then join the results on the artist and venue tables to get those dimension values. (Basically, search for the entity id's in a sub query, then in an outer query get the dimension values you need. Keeping them separate should improve things...)
DECLARE @artist_filter TABLE (
tag_id INT
)
DECLARE @venue_filter TABLE (
tag_id INT
)
INSERT INTO @artist_filter
SELECT id FROM tag
WHERE name IN ('techno','trombone')
INSERT INTO @venue_filter
SELECT id FROM tag
WHERE name IN ('cheap-beer','great-most-pits')
SELECT
concert.id AS concert_id,
concert.date AS concert_date,
artist.id AS artist_id,
venue.id AS venue_id
FROM
concert
INNER JOIN
artist_tag
ON artist_tag.artist_id = concert.artist_id
INNER JOIN
@artist_filter AS [artist_filter]
ON [artist_filter].tag_id = artist_tag.id
INNER JOIN
venue_tag
ON venue_tag.venue_id = concert.venue_id
INNER JOIN
@venue_filter AS [venue_filter]
ON [venue_filter].tag_id = venue_tag.id
WHERE
concert.date BETWEEN NOW() AND (NOW() + INTERVAL 1 MONTH)
GROUP BY
concert.id,
concert.date,
artist_tag.artist_id,
venue_tag.id
HAVING
COUNT(DISTINCT [artist_filter].id) = (SELECT COUNT(*) FROM @artist_filter)
AND
COUNT(DISTINCT [venue_filter].id) = (SELECT COUNT(*) FROM @venue_filter)
(I'm on a netbook and suffering for it, so I'll leave out the outer query getting the artist and venue names from the artist and venue tables grin)
EDIT
Note:
Another option would be to filter the artist_tag and venue_tag tables in sub-queries/derived-tables. Whether this is worth it depends on how influential the join on the Concert table is. My assumption here is that there are MANY artist and venues, but once filtered on the concert table (itself filtered by the dates) the number of artists/venues decreases dramatically.
Also, there is often a need/desire to deal with the case where NO artist_tags and/or venue_tags are specified. From experience it is better to deal with this programatically. That is, use IF statements and queries specially suited to those cases. A single SQL query CAN be written to handle it, but is much slower than the programatic alternative. Equally, writing similar queries several times may look messy and degrade maintainability, but the increase in complexity need to get this to be a single query is often harder to maintain.
EDIT
Another similar layout could be...
- Filter concert by artist as sub_query/derived_table
- Filter results by venue as sub_query/derived_table
- Join results on dimension tables to get names, etc
(Cascaded filtering)
SELECT
<blah>
FROM
(
SELECT
<blah>
FROM
(
SELECT
<blah>
FROM
concert
INNER JOIN
artist_tag
INNER JOIN
artist_filter
WHERE
GROUP BY
HAVING
)
INNER JOIN
venue_tag
INNER JOIN
venue_filter
GROUP BY
HAVING
)
INNER JOIN
artist
INNER JOIN
venue
By cascading the filtering, each subsequent filtering has a reduce set it has to work on. This MAY reduce the work done by the GROUP BY - HAVING section of the query. For two levels of filtering I would guess this to be unlikely to be dramatic.
The original may still be more performant as it benefits additional filtering in a different manner. In your example:
- There may be many artist in your date range, but few which meet at least one criteria
- There may be many venues in your date range, but few which meet at least one criteria
- Before the GROUP BY, however, all concerts are eliminated where...
---> the artist(s) meets NONE of the criteria
---> AND/OR the venue meets NONE of the criteria
Where you are searching by many criteria this filtering degrades. Also where venues and/or artists share a lot of tags, the filtering also degrades.
So when would I use the original, or when would I use the Cascaded version?
- Original : Few search criteria and venues/artists are dis-similar from each other
- Cascaded : Lots of search criteria or venues/artists tend to be similar