如何用 SQL 求出「都关注了某100人」和「都收藏了某100帖子」的所有用户?
@老虎会游泳,另外,多个条件的『关系除法』,我目前会写成这个样子:
WITH
除以要求技能表(员工ID) AS (
SELECT DISTINCT 员工ID
FROM 员工技能表 AS ES1
WHERE NOT EXISTS (
SELECT *
FROM 要求技能表
WHERE NOT EXISTS (
SELECT *
FROM 员工技能表 AS ES2
WHERE ES2.员工ID = ES1.员工ID
AND ES2.技能 = 要求技能表.技能 ))
),
除以要求手机表(员工ID) AS (
SELECT DISTINCT 员工ID
FROM 员工手机表 AS ES1
WHERE NOT EXISTS (
SELECT *
FROM 要求手机表
WHERE NOT EXISTS (
SELECT *
FROM 员工手机表 AS ES2
WHERE ES2.员工ID = ES1.员工ID
AND ES2.手机 = 要求手机表.手机 ))
)
-- 如果要显示员工详细信息的话
SELECT 员工表.*
FROM 除以要求技能表
JOIN 除以要求手机表 USING(员工ID)
JOIN 员工表 USING(员工ID)
GROUP BY 员工ID;
然后我就看着这两坨除以要求技能表、除以要求手机表很不爽,但又不会高效合并。。
@无名啊,那你为什么不用它呢?总有一天你会发现拼接SQL才是让一切更简单的正确方法,因为它把复杂的变参处理任务从MySQL转移到了外部程序,MySQL不必自己处理它,得到的SQL自然可以干净整洁。
SELECT * FROM 员工信息表 WHERE
员工ID IN (SELECT 员工ID FROM 员工技能表 WHERE 技能 = '技能1')
AND 员工ID IN (SELECT 员工ID FROM 员工技能表 WHERE 技能 = '技能2')
AND 员工ID IN (SELECT 员工ID FROM 员工手机表 WHERE 手机 = '手机1')
AND 员工ID IN (SELECT 员工ID FROM 员工手机表 WHERE 手机 = '手机2');
@老虎会游泳,我怀疑,你的 SQL 改成这样,可以快点儿(说不定连临时表也没了)
SELECT *
FROM 员工表
WHERE EXISTS (SELECT * FROM 员工技能表 WHERE 员工ID = 员工表.ID AND 技能 = '技能1')
AND EXISTS (SELECT * FROM 员工技能表 WHERE 员工ID = 员工表.ID AND 技能 = '技能2')
AND EXISTS (SELECT * FROM 员工手机表 WHERE 员工ID = 员工表.ID AND 手机 = '手机1')
AND EXISTS (SELECT * FROM 员工手机表 WHERE 员工ID = 员工表.ID AND 手机 = '手机2');
@无名啊,我顺便来猜测一下你的“题目”中为什么会出现“要求技能表”,肯定是因为这是一门数据库课程,要秉承能不涉及外部程序就不涉及外部程序的教学思路。
那么,没有外部程序,可变参数怎么输入呢?
放进一个临时表呗!
这就是“要求技能表”的来源。
然后,因为参数全存在表里了,然后还要匹配全部条件,那怎么办呢,“这是数据库课,不能依靠外部程序啊”,所以就写了这么个复杂的语句:
SELECT DISTINCT 员工ID
FROM 员工技能表 AS ES1
WHERE NOT EXISTS (
SELECT *
FROM 要求技能表
WHERE NOT EXISTS (
SELECT *
FROM 员工技能表 AS ES2
WHERE ES2.员工ID = ES1.员工ID
AND ES2.技能 = 要求技能表.技能 ))
而在实际的编程中,我肯定不会选择创建一个要求技能表
再把参数塞进去。实际编程必然是使用其他编程语言的,所以把一系列AND 参数=?
拼接到SQL语句里显然更容易,SQL语句运行起来想必也更快。
@无名啊,我写错了,应该是=
而不是LIKE
,我的LIKE
是从虎绿林帖子那个语句复制的,忘记改了。
我认为
SELECT * FROM 员工信息表 WHERE
员工ID IN (SELECT 员工ID FROM 员工技能表 WHERE 技能 = '技能1')
AND 员工ID IN (SELECT 员工ID FROM 员工技能表 WHERE 技能 = '技能2')
AND 员工ID IN (SELECT 员工ID FROM 员工手机表 WHERE 手机 = '手机1')
AND 员工ID IN (SELECT 员工ID FROM 员工手机表 WHERE 手机 = '手机2');
应该快于
SELECT *
FROM 员工表
WHERE EXISTS (SELECT * FROM 员工技能表 WHERE 员工ID = 员工表.ID AND 技能 = '技能1')
AND EXISTS (SELECT * FROM 员工技能表 WHERE 员工ID = 员工表.ID AND 技能 = '技能2')
AND EXISTS (SELECT * FROM 员工手机表 WHERE 员工ID = 员工表.ID AND 手机 = '手机1')
AND EXISTS (SELECT * FROM 员工手机表 WHERE 员工ID = 员工表.ID AND 手机 = '手机2');
因为(SELECT * FROM 员工手机表 WHERE 员工ID = 员工表.ID AND 手机 = '手机2')
是一个INNER JOIN
(内连接),而(SELECT 员工ID FROM 员工技能表 WHERE 技能 = '技能1')
只是一个简单的单表索引查询。我想不到单表索引查询能比内连接慢的场景。
至于
说不定连临时表也没了
这有可能吗?每当我们使用括号()
,就会生成一个临时表,这可以避免吗?
@老虎会游泳,MySQL 8.0.17+ 有多值索引(就是在数组上建索引,但每个索引最多只能有一个多值字段)
https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-multi-valued
@无名啊,那不一样,如果深究起来,每个括号
()
都构成一个临时表,你给的语句临时表一点不比我少。但是你的要求技能表
必须你自己创建并且填充查询条件,我的可不用,查询条件是内联在SQL中的。根据计算机的一般规律,内联通常比外置快。