您的 NOT EXIST 查询非常接近。您所缺少的只是子查询与 characterID 上的外部查询之间的相关性。
我刚刚c
在您的外部查询中为表添加了别名,在子查询中为表添加了别名d
,并在子查询中的 WHERE 子句中添加了一个谓词
SELECT characterID FROM CHARACTERS c
WHERE NOT EXISTS (SELECT * FROM challenges d
WHERE d.userCharID = '610'
AND d.chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND d.characterID = c.characterID)
这里的“技巧”是d.characterID
(从子查询中的表)到c.characterID
(从外部查询中的表)的相关匹配。
因此,查询正在检查该外部表中的每个字符,我们的用户在过去 24 小时内是否与该用户有过挑战。因此,此查询将返回您指定的结果集。
但是......如果您有一组相对较大的字符,而一个相对较小的字符集受到挑战,这不太可能是返回结果集的最快查询。
获取结果集的另一种方法是使用带有 IS NULL 谓词的 LEFT JOIN(我们将其称为“反连接”)。如果此查询:
SELECT d.characterID
FROM challenges d
WHERE d.userCharID = 642
AND d.chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
GROUP BY d.characterID
返回已被挑战的所有 characterID 的列表,这是您要从所有字符集中排除的字符集,然后您可以将该查询用作内联视图,如下所示:
SELECT n.characterID
FROM characters n
LEFT
JOIN (
SELECT d.characterID
FROM challenges d
WHERE d.userCharID = 642
AND d.chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
GROUP BY d.characterID
) c
ON c.characterID = n.characterID
WHERE c.characterID IS NULL
在这里,我们获取所有字符 (n) 的列表,并将它们与已被挑战的字符列表进行匹配(子查询别名为 c)。我们使用LEFT JOIN
操作是因为我们想要字符表中的所有行,无论是否找到匹配项。
WHERE 子句然后抛出我们找到匹配的所有行,所以我们剩下的是没有被挑战的字符集。
在我对大集合的测试中,这通常会优于 aNOT EXISTS
和 a NOT IN
(当有适当的索引可用时)。但有时我发现 aNOT IN
更快,有时 theNOT EXISTS
更快。
我发现最好将所有三种方法都“装在口袋里”,并使用最合适的方法。我通常从反连接模式开始(这是我习惯写的),然后测试NOT EXISTS
和NOT IN
比较性能。