我想选择你的角色在过去24小时内没有挑战过的所有角色。
SELECT * FROM challenges
WHERE userCharID = 642 AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
这将返回几行,其中包含您的角色在过去一天发起的挑战
SELECT characterID FROM CHARACTERS
WHERE NOT EXISTS (SELECT * FROM challenges
WHERE userCharID = '610'
AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY))
我是否使用WHERE NOT EXISTS错误?
答案 0 :(得分:3)
我是否使用WHERE NOT EXISTS错误?
是。你想使用NOT IN而不是NOT EXISTS。如果使用NOT EXISTS并且非存在子查询返回任何行,则条件将为false,并且主查询不会返回任何数据。如果没有返回任何行,则条件将为true,并且主查询将返回所有行(因为,在此示例中,主查询中没有其他条件)。通常,NOT EXISTS中的子查询是相关的子查询,因此必须为每一行评估子查询。在这里,您没有相关的子查询(这对性能有利)。但是您的查询意味着“返回有关所有角色的信息,除非在指定用户的最后一天内有一些角色受到挑战。”
(在此分析中,我已经悄悄地更改了SQL,以便始终将userCharID
与字符串进行比较,并特别使用值'642'
进行比较。)
选择过去24小时内角色[ ]挑战的所有角色:
SELECT * FROM Challenges WHERE userCharID = '642' AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
这会返回几行,其中包含您的角色在过去一天发起的挑战。
因此,要找到您未挑战的所有人,您需要选择除了您所挑战的列表中的所有用户之外的所有用户,这些用户将转换为:
SELECT characterID
FROM Characters
WHERE userCharID NOT IN
(SELECT userCharID
FROM Challenges
WHERE userCharID = '642'
AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
)
这应该会为您提供在过去24小时内未挑战过的(可能相当大的)角色列表。
答案 1 :(得分:2)
WHERE NOT EXISTS根据结果返回TRUE或FALSE。
如果子查询完全返回任何行,则EXISTS子查询为TRUE,NOT EXISTS子查询为FALSE。
在你的情况下,这意味着如果
(SELECT * FROM challenges
WHERE userCharID = '610' AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY))
然后返回所有行
您的查询将被评估为
SELECT characterID FROM CHARACTERS WHERE FALSE;
这显然不是你想要的。
您可以改为使用IN运算符:
SELECT characterID FROM CHARACTERS
WHERE characterID NOT IN (SELECT characterID FROM challenges
WHERE userCharID = '610' AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY))
第二个characterID
(子查询中的那个)需要是与CHARACTERS表中的characterID相对应的字段,这可能是userCharID,但我怀疑它,给你的where子句。没有架构,我无法确定。
您可以使用其他选项直接选择from the subquery,或者在某些情况下通过joins获取数据。
答案 2 :(得分:0)
您的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子句会抛出我们找到匹配项的所有行,所以剩下的就是没有被质疑的字符集。
在我使用大集测试时,这通常会优于NOT EXISTS
和NOT IN
(当适当的索引可用时)。但有时我发现NOT IN
更快,有时NOT EXISTS
更快。
我觉得将所有三种方法“放在口袋里”都很好,并使用最合适的方法。我通常从反连接模式开始(这是我以前写的),然后测试NOT EXISTS
和NOT IN
来比较性能。