Эффективный способ объединения и пересечения в MySQL

У меня есть таблица mySQL со столбцами: имя и метка. Если у человека «Боб» есть ярлыки «крутой», «забавный» и «ребяческий», в моей таблице будут соответствующие строки: (Боб, крутой), (Боб, смешной) и (Боб, детский).

Есть ли эффективный способ выбрать людей на основе меток с помощью логического запроса? Например, в псевдо-SQL: ВЫБЕРИТЕ имя, ГДЕ человек (КРУТОЙ ИЛИ НЕ СМЕШНОЙ), А НЕ ДЕТСКИЙ.

Я думаю, что мог бы взломать что-то вместе, используя UNION, JOIN, может быть, некоторые подзапросы, но мне было интересно, есть ли эффективный способ сделать это.

РЕДАКТИРОВАТЬ:

На данный момент я планирую распространять И, т.е. ((КРУТО ИЛИ НЕ СМЕШНО) И НЕ ПО-ДЕТСКИ) => (КРУТО И НЕ ПО-ДЕТСКИ) ИЛИ (НЕ СМЕШНО И НЕ ПО-ДЕТСКИ). И тогда я могу определить каждую из частей, которые объединены ИЛИ вместе с чем-то вроде:

SELECT DISTINCT a.name
FROM `tags` AS a
JOIN `tags` AS b ON (a.label='cool' AND a.name=b.name AND b.name NOT IN (
    SELECT name FROM `tags` WHERE label='funny'))
JOIN `tags` AS c ON (a.name=c.name AND c.name='childish')
# for "COOL AND NOT FUNNY AND CHILDISH"

А затем используйте UNION, чтобы соединить их вместе.


person Saif Hakim    schedule 29.06.2011    source источник
comment
Я думаю, что это настолько эффективно, насколько это возможно. Я не знаю другого способа сделать его более эффективным и не могу придумать.   -  person adamjmarkham    schedule 29.06.2011


Ответы (1)


Для отрицательных проверок наиболее эффективным способом было бы использование MINUS следующим образом:

SELECT NAME
FROM NAME_LABEL
WHERE LABEL IN ('COOL') -- use IN for easy matching of multiple labels
UNION
SELECT NAME
FROM NAME_LABEL NL
WHERE NOT EXISTS (SELECT * FROM NAME_LABEL WHERE NAME = NL.NAME AND LABEL IN ('FUNNY')) 
MINUS
SELECT NAME
FROM NAME_LABEL
WHERE LABEL IN ('CHILDISH');

Ключевое слово MINUS выбирает отдельные строки из первого запроса и не появляется во втором запросе.

Производительность была бы лучше с индексом LABEL:

CREATE INDEX NAME_LABEL_NAME ON NAME_LABEL(NAME);

К сожалению, для "НЕ СМЕШНО" требуется подзапрос EXISTS. Если вы используете соединение, оптимизатор запросов MySQL в любом случае превратит его в подзапрос :(

person Bohemian♦    schedule 29.06.2011
comment
Тогда для создания любого логического выражения будет достаточно MINUS, INTERSECTION и UNION? - person Saif Hakim; 29.06.2011
comment
Как WHERE NOT EXISTS сравнивается с SELECT * MINUS FUNNY с точки зрения эффективности? Извините, я только изучаю SQL. - person Saif Hakim; 29.06.2011
comment
Это настолько хорошо, насколько это возможно. К сожалению, часть EXISTS() не очень эффективна. INTERSECTION означает AND логику между наборами строк, которая вам не нужна. UNION просто складывает два набора строк вместе, сортируя их и отбрасывая дубликаты (обратите внимание, что UNION ALL не отбрасывает дубликаты и сохраняет порядок строк). MINUS удаляет совпадающие строки из предыдущего набора строк. - person Bohemian♦; 29.06.2011
comment
mySQL не поддерживает MINUS/INTERSECT. Оставляет ли это меня с подзапросами? - person Saif Hakim; 30.06.2011