2

我想选择你的角色在过去 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 错误吗?

4

3 回答 3

3

我使用 WHERE NOT EXISTS 错误吗?

是的。您想使用 NOT IN 而不是 NOT EXISTS。如果您使用 NOT EXISTS 并且不存在的子查询返回任何行,则条件将为 false 并且主查询不会返回任何数据。如果没有返回任何行,则条件为真,并且所有行都将由主查询返回(因为在此示例中,主查询中没有其他条件)。通常,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 小时内未挑战过的角色列表。

于 2012-07-03T00:35:31.803 回答
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 子句。没有架构,我无法确定。

您可以使用的其他选项是直接从子查询中选择,或者在某些情况下通过连接获取数据。

于 2012-07-03T00:22:42.313 回答
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 子句然后抛出我们找到匹配的所有行,所以我们剩下的是没有被挑战的字符集。


在我对大集合的测试中,这通常会优于 aNOT EXISTS和 a NOT IN(当有适当的索引可用时)。但有时我发现 aNOT IN更快,有时 theNOT EXISTS更快。

我发现最好将所有三种方法都“装在口袋里”,并使用最合适的方法。我通常从反连接模式开始(这是我习惯写的),然后测试NOT EXISTSNOT IN比较性能。

于 2012-07-19T16:04:20.850 回答