【问题标题】:Efficient way to do unions and intersections in mySQL在 mySQL 中进行联合和交叉的有效方法
【发布时间】:2011-06-29 02:38:25
【问题描述】:

我有一个带有列的 mySQL 表:名称和标签。如果一个人“Bob”有标签“cool”、“funny”和“childish”,我的表将有相应的行:(Bob,cool)、(Bob,funny)和(Bob,childish)。

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

我想我可以使用 UNION、JOIN 或者一些子查询来组合一些东西,但我想知道是否有一种有效的方法来做到这一点。

编辑:

截至目前,我计划分发 AND,即((酷或不有趣)而不是幼稚)=>(酷而不是幼稚)或(不有趣而不是幼稚)。然后我可以确定每个部分与以下内容进行或运算:

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 将它们连接在一起。

【问题讨论】:

  • 我认为这已经是最有效的了。我不知道任何其他方法可以提高效率,也想不出任何方法。

标签: mysql logic boolean union intersection


【解决方案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);

很遗憾,“NOT FUNNY”需要一个 EXISTS 子查询。如果你使用连接,MySQL 查询优化器无论如何都会把它变成一个子选择:(

【讨论】:

  • 要创建任何“逻辑表达式”,MINUS、INTERSECTION 和 UNION 就足够了吗?
  • 在效率方面,WHERE NOT EXISTS 与 SELECT * MINUS FUNNY 相比如何?抱歉,我只是在学习 SQL。
  • 尽其所能。不幸的是,EXISTS() 部分效率不高。 INTERSECTION 表示行集之间的AND 逻辑,您不需要。 UNION 只需将两个行集相加,对它们进行排序并丢弃重复项(请注意,UNION ALL 不会丢弃重复项并保留行顺序)。 MINUS 从前面的行集中删除匹配的行。
  • mySQL 不支持 MINUS/INTERSECT。这会给我留下子查询吗?
猜你喜欢
  • 1970-01-01
  • 2015-03-07
  • 1970-01-01
  • 2015-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-31
  • 1970-01-01
相关资源
最近更新 更多