在mySQL中进行联合和交叉的有效方法

时间:2011-06-29 02:38:25

标签: mysql logic boolean union intersection

我有一个带有列的mySQL表:名称和标签。如果一个人,“鲍勃”有标签“酷”,“有趣”和“幼稚”,我的表将有相应的行:(鲍勃,酷),(鲍勃,搞笑),和(鲍勃,幼稚)。

是否有一种基于带布尔查询的标签选择人的有效方法?例如,在伪SQL中:SELECT name WHERE person IS(COOL OR NOT FUNNY)AND NOT CHILDISH。

我想我可以使用UNION,JOIN和一些子查询一起破解一些东西,但我想知道是否有一种有效的方法来做到这一点。

编辑:

截至目前,我正计划分发AND,即((酷或不有趣)而不是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 个答案:

答案 0 :(得分:2)

对于否定性检查,最有效的方法是使用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);

不幸的是,“NOT FUNNY”需要一个EXISTS子查询。如果使用连接,MySQL查询优化器仍会将其转换为子选择:(