Это то, что хотя бы раз возникает у инженеров, работающих с веб-приложениями. Есть несколько способов добиться этого, с разными достоинствами и недостатками.
Заявление об ограничении ответственности
Эта статья посвящена хранению тегов в базе данных SQL; это не означает, что это лучший или единственный способ сделать это. Фактически, есть много других возможностей, доступных в таких системах, как Redis, которые делают фантастическую работу с этим. Однако, если вы застряли в SQL, чувствуете, что нет необходимости использовать другой инструмент или просто имеете существующие решения, подобные приведенным ниже, это может быть полезно в качестве шпаргалки.
Я буду стремиться использовать ANSI SQL, чтобы он оставался максимально переносимым, и сосредоточусь на реализации и понимании, а не на злоупотреблении конкретными функциями различных механизмов SQL, которые могут сделать его более эффективным.
Давайте перейдем к отношениям
Большинство людей порекомендуют, если вы используете реляционный язык, такой как SQL, вы должны хранить данные в реляционной манере, имеет смысл, верно? Если вы сделали это до того, как следующий код будет иметь смысл и даже может показаться очень знакомым:
CREATE TABLE artist ( artist_id INTEGER, name VARCHAR(64), PRIMARY KEY (artist_id) ); CREATE TABLE artist_tag ( artist_id INTEGER, tag VARCHAR(16), PRIMARY KEY (artist_id, tag) );
У вас может быть третья таблица посередине, которая объединяет уникальные теги с художниками, и это не такой уж большой шаг вперед по сравнению с тем, что у нас есть - я просто пока буду упрощен.
Теги в отношении ИЛИ; то есть получить любого исполнителя, у которого есть хотя бы один из следующих тегов, довольно просто:
-- Find all the artists from any of the 3 decades. SELECT DISTINCT artist_id, name FROM artist JOIN artist_tag USING (artist_id) WHERE tag IN ('60s', '70s', '80s’);
Большинство людей придерживаются отношения И; именно здесь вы хотите, чтобы исполнитель содержал все теги в данном наборе. Некоторые говорят об этом - вероятно, самый простой - добавить дополнительное предложение HAVING:
-- We only want artists that are british AND rock. SELECT artist_id, name FROM artist JOIN artist_tag USING (artist_id) WHERE tag IN ('british', 'rock') GROUP BY artist_id HAVING count(*) = 2;
count(*) = 2
более гибкий, чем вы думаете. Он также позволяет вам указать, сколько тегов должно соответствовать, чтобы вы могли сказать любые 3 из 5 тегов:
WHERE tag IN ('british', 'rock', '60s', 'pop', 'alternative') HAVING count(*) >= 3
Если ваши теги не уникальны (как в этих примерах), вам просто нужно указать теги как DISTINCT
:
WHERE tag IN ('british', 'rock') HAVING count(DISTINCT tag) = 2
Менее желательный способ - использовать JOIN для каждого из тегов. Я говорю «менее желательно», потому что это может привести к более сложному SQL-запросу, который будет сложно построить. Однако, если у вас есть много тегов, которые нужно пересекать, и вы знаете, что хотя бы один тег встречается очень редко, планировщик запросов может сделать это более эффективно, чем описанный выше метод.
SELECT artist_id, name FROM artist JOIN artist_tag AS tag1 USING (artist_id) JOIN artist_tag AS tag2 USING (artist_id) WHERE tag1 = 'british' AND tag2 = 'rock’;
Первоначально опубликовано на http://elliot.land 26 ноября 2015 г.