Эффективный способ сделать союзы и пересечения в MySQL - PullRequest
0 голосов
/ 29 июня 2011

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

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

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

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

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

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, чтобы соединить их вместе.

1 Ответ

2 голосов
/ 29 июня 2011

Для отрицательных проверок наиболее эффективным способом было бы использовать 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 все равно превращает его в подвыбор: (

...