Мой подход немного более общий, я помещаю параметры фильтра в таблицы и затем использую GROUP BY, HAVING и COUNT для фильтрации результатов. Я использовал этот базовый подход несколько раз для какого-то очень сложного «поиска», и он работает очень хорошо (для меня ухмылка ).
Я также изначально не включаю таблицы измерений Artist и Venue. Я получаю результаты в виде идентификаторов (просто требуются artist_tag и venue_tag), а затем объединяю результаты в таблицах Artist и Venue для получения этих значений измерений. (По сути, ищите идентификаторы сущностей в подзапросе, затем во внешнем запросе получите значения измерений, которые вам нужны. Хранение их отдельно должно улучшить ситуацию ...)
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)
(я нахожусь на нетбуке и мучаюсь за него, поэтому я пропущу внешний запрос, получая имена артистов и мест проведения из таблиц исполнителей и мест проведения мероприятий ухмылка )
EDIT
Примечание:
Другим вариантом будет фильтрация таблиц artist_tag и venue_tag в подзапросах / производных таблицах. Стоит ли это того, зависит ли то, насколько влиятельным является объединение на концертном столе. Мое предположение здесь состоит в том, что существует МНОГИЕ исполнители и места проведения, но после фильтрации на концертном столе (сама фильтруется по датам) количество исполнителей / мест встречи резко уменьшается.
Кроме того, часто возникает необходимость / желание иметь дело со случаем, в котором не указываются NO artist_tags и / или venue_tags. По опыту лучше заниматься этим программно. То есть используйте операторы IF и запросы, специально подходящие для этих случаев. Один SQL-запрос МОЖЕТ быть написан для его обработки, но он намного медленнее, чем программная альтернатива. Точно так же, написание похожих запросов несколько раз может показаться беспорядочным и ухудшать удобство сопровождения, но из-за усложнения необходимости сделать его единым запросом зачастую сложнее в обслуживании.
EDIT
Еще один похожий макет может быть ...
- Фильтровать концерт по исполнителю как sub_query / производная_таблица
- Отфильтруйте результаты по месту проведения как sub_query / производная_таблица
- Объедините результаты в таблицах измерений, чтобы получить имена и т. Д.
(каскадная фильтрация)
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
При каскадной фильтрации каждая последующая фильтрация имеет набор сокращений, с которым она должна работать. Это МОЖЕТ сократить объем работы, выполняемой разделом GROUP BY - HAVING запроса. Я полагаю, что для двух уровней фильтрации это вряд ли будет драматичным.
Оригинал может быть еще более производительным, поскольку он обеспечивает дополнительную фильтрацию другим способом. В вашем примере:
- В вашем диапазоне дат может быть много артистов, но немногие соответствуют хотя бы одному критерию
- В вашем диапазоне дат может быть много мест, но мало, которые соответствуют хотя бы одному критерию
- Однако до GROUP BY все концерты исключаются, где ...
---> исполнитель (и) не соответствует ни одному из критериев
---> И / ИЛИ место проведения не соответствует ни одному из критериев
Если вы ищете по многим критериям, эта фильтрация ухудшается. Кроме того, там, где места проведения и / или артисты используют много тегов, фильтрация также ухудшается.
Так, когда я использовал бы оригинал, или когда я бы использовал Каскадную версию?
- Оригинал: несколько критериев поиска и мест / исполнителей не похожи друг на друга
- Каскадный: множество критериев поиска или мест / исполнителей, как правило, похожи