反连接模式就足够了。
看起来“a isafriend of b”在friends 表中可以用一行表示,(a,b,2) 或(b,a,2)。 (这对我来说并不完全清楚,但我现在假设这是真的。)
这个查询只是第一次切割。为了在朋友表中双向检查朋友,我将在连接谓词中使用OR 条件。
这实际上是从users 获取所有行,以及从friends 获取“匹配”行。 “技巧”是WHERE 子句中的谓词,它消除了在朋友中找到“匹配”的所有行,只留下users 中没有“匹配”的那些行。
SELECT u.user_id
, u.name
FROM users u
LEFT
JOIN friends f
ON ( f.user_id = u.user_id AND f.friend_id = ? AND f.status = 2 )
OR ( f.friend_id = u.user_id AND f.user_id = ? AND f.status = 2 )
WHERE f.user_id IS NULL
OR f.friend_id IS NULL
不幸的是,MySQL 优化器通常不会太优雅地处理这些OR 条件。作为重新编写的另一个剪辑,我们可以尝试将与朋友的联接拆分为两个左联接。
我认为这是等价的:
SELECT u.user_id
, u.name
FROM users u
LEFT
JOIN friends f
ON ( f.user_id = u.user_id AND f.friend_id = ? AND f.status = 2 )
LEFT
JOIN friends g
OR ( g.friend_id = u.user_id AND g.user_id = ? AND g.status = 2 )
WHERE f.user_id IS NULL
AND g.friend_id IS NULL
(我还没有测试第二个查询...目标是做同样的事情。来自users 的所有行,以及来自friends 的匹配行,然后过滤掉所有具有match.
编辑
我认为我们还需要从“特定用户”users 中过滤掉该行。上面的查询会将用户自己返回为他自己的“不是朋友”。为此,我们可以添加到WHERE 子句
AND u.user_id <> ?
对于第一个查询,我们需要注意 OR 和 AND 布尔运算符之间的优先顺序...我认为我们需要将两个 OR'd 条件包装在括号中的第一个查询。
跟进
还有一些其他查询模式可以返回等效结果。作为两个例子,我们可以使用NOT IN (subquery) 形式的谓词,或者NOT EXISTS (subquery) 形式的谓词。
使用“NOT IN(子查询)”
使用这种形式,请注意子查询不会返回 NULL 值。如果列表中有 NULL 值,谓词将不返回 TRUE。
SELECT u.user_id
, u.name
FROM users u
WHERE u.user_id NOT IN ( SELECT f.user_id
FROM friends f
WHERE f.user_id IS NOT NULL
AND f.status = 2
AND f.friend_id = ?
)
AND u.user_id NOT IN ( SELECT f.friend_id
FROM friends f
WHERE f.friend_id IS NOT NULL
AND f.status = 2
AND f.user_id = ?
)
AND u.user_id <> ?
使用“不存在(子查询)”
SELECT u.user_id
, u.name
FROM users u
WHERE NOT EXISTS ( SELECT 1
FROM friends f
WHERE f.user_id = u.user_id
AND f.status = 2
AND f.friend_id = ?
)
AND NOT EXISTS ( SELECT 1
FROM friends g
WHERE g.friend_id = u.user_id
AND g.status = 2
AND g.user_id = ?
)
AND u.user_id <> ?