这是使用 EXISTS 的正确方法吗?
Posted
技术标签:
【中文标题】这是使用 EXISTS 的正确方法吗?【英文标题】:Is this the correct way to use EXISTS? 【发布时间】:2012-02-28 01:53:40 【问题描述】:我一直在为星期四的考试做一些 SQL,我怀疑我是否正确使用了 EXISTS 语句。
所以,这里我有一个有 2 个表的数据库
Machines Maintenance
============ ==============
PK ID_Machine PK ID_Machine FK
Name PK ID_Task FK
Date_bought Date
所以,他们希望我写的查询是 “显示 2011 年未接受任何维护的最旧机器的所有数据”
我的做法是这样的:
SELECT M.ID_MACHINE, M.NAME, M.DATE_BOUGHT
FROM MACHINES M
WHERE NOT EXISTS (SELECT MA.*
FROM MAINTENANCE MA
WHERE MA.ID_MACHINE = M.ID_MACHINE
AND YEAR(MA.DATE) = 2011)
AND EXISTS (SELECT MIN(M2.DATE_BOUGHT)
FROM MACHINE M2
WHERE M2.ID_MACHINE = M.ID_MACHINE)
这是执行此查询的正确方法吗?我在 EXISTS 语句中使用 SELECT MIN() 有意义吗?
提前感谢大家!
【问题讨论】:
嗯....当您尝试运行查询时会发生什么? 嗯,问题是我实际上没有在任何 DBMS 中创建这个数据库。这只是教科书中的一个练习,我正在一张纸上解决它。不幸的是,教科书没有答案,所以这就是我问的原因:) 首先,您需要一个AND
而不是第二个WHERE
。另外,永远不要在 EXISTS
中使用 SELECT MIN()
- 这没有任何意义。
@Bohemian 是的,你完全正确,我错过了那里的 AND。另外,这基本上是我的疑问。我认为通过将“SELECT MIN()”放在 EXISTS 语句中,它会删除所有其他行,但具有 MIN(Date_Bought) 的行除外。我想我当时错了!
你在exists
中使用的第三张表machine m2
是什么?
【参考方案1】:
当您使用exists时,它只验证为过滤器(连接和位置)返回了一条数据。通常,您会看到带有 select 1 from... 的存在查询。这是因为没有使用实际的返回值。
这是一个新颖的想法,我必须亲自进行测试。然而,正如我上面所说,返回数据在很大程度上被忽略了。它只关心连接和过滤器匹配的位置,而不关心 MIN,即使它本身似乎是一个过滤器。它更像是一种聚合,所以基础数据似乎仍然存在。第一个存在是有效的,但下一部分确实需要工作。我已经在下面更新了我会做什么。
SELECT M.ID_MACHINE, M.NAME, M.DATE_BOUGHT
FROM MACHINES M
WHERE NOT EXISTS (SELECT 1
FROM MAINTENANCE MA
WHERE MA.ID_MACHINE = M.ID_MACHINE
AND YEAR(MA.DATE) = 2011)
AND M.ID_MACHINE = (SELECT TOP 1 M2.ID_MACHINE
FROM MACHINE M2
WHERE M2.ID_MACHINE = M.ID_MACHINE
ORDER BY M2.DATE_BOUGHT)
【讨论】:
对,那么就我而言,在 EXISTS 条件下,我试图选择最旧的机器……这就是为什么我在子查询的选择语句中包含一个 MIN() 的原因。我认为通过这样做,EXISTS 语句将消除所有其他机器,但其中包含 MIN(Date_bought) 的机器。是这样吗?还是我以错误的方式解决这个问题? 更新我的回复,但简短的回答是您确实以错误的方式解决了这个问题。虽然这是一个新颖的想法:) 对不起,忽略我的最后一条评论,我没有把你所有的评论都读到最后,只是第一段。那么,你认为它可能会这样工作吗?我是这么认为的,因为正如您所说, MIN() 理论上可以充当过滤器。只要有机会,我会尝试在实际数据库中对此进行测试。 我的回复已更新,以解释为什么 MIN 在测试并仔细考虑后实际上无法正常工作。至于存在,我会遵守 SELECT 1 的约定,以便您始终记住返回的实际数据与它返回的事实(即返回数据存在)无关紧要。主要是聚合效应把你(和我自己)搞砸了。如果您喜欢该回复,请不要忘记投票并选择一个通过验收检查最准确地回答您的问题的回复:) 请注意,SELECT TOP 1
是 TSQL 而不是标准的。 FETCH FIRST
是标准...但不需要回答问题。【参考方案2】:
引用SQL-92 standard:
8.8
Function Specify a test for a non-empty set. Format <exists predicate> ::= EXISTS <table subquery> Syntax Rules None. Access Rules None. General Rules 1) Let T be the result of the <table subquery>. 2) If the cardinality of T is greater than 0, then the result of the <exists predicate> is true; otherwise, the result of the <exists predicate> is false. Leveling Rules 1) The following restrictions apply for Intermediate SQL: None. 2) The following restrictions apply for Entry SQL in addition to any Intermediate SQL restrictions: None.
所以,不,子查询的语法没有特殊规则(只有它是有效的)。 exists
语句只关心它是否返回任何行。
【讨论】:
好的,所以在这种情况下,EXISTS 语句将为 M2 表中的一个特定行返回 true。由于该表在我的查询中具有对主表的外部引用,这是否意味着通过这样做,它将排除我的主表中的所有行,但具有 MIN(Date_bought) 值的行除外?因为这基本上就是我的目标 不,它不会从任何表中删除任何行。它既不是delete
也不是 truncate
声明。【参考方案3】:
将 EXISTS
和 NOT EXISTS
视为布尔条件,您可以将其附加到查询 where 子句中。它们用于检查与您正在查看的数据相关的其他数据条件是真还是假。
SELECT M.ID_MACHINE, M.NAME, M.DATE_BOUGHT
FROM MACHINES M
-- DO NOT want a machine with a maintenance year of 2011
WHERE NOT EXISTS (SELECT 1
FROM MAINTENANCE MA
WHERE MA.ID_MACHINE = M.ID_MACHINE
AND YEAR(MA.DATE) = 2011)
-- DO want there to be a matching ID in the Machine table
WHERE EXISTS (SELECT 1
FROM MACHINE M2
WHERE M2.ID_MACHINE = M.ID_MACHINE)
正如贾斯汀所说,子查询的返回值没有被使用,所以SELECT 1
是EXISTS/NOT EXISTS
的约定。
【讨论】:
但是,当我刚刚编辑我的帖子时,MIN 本身就像一个过滤器......所以我想知道是否有 MIN 会起作用......我从来没有尝试过,理论上它可以工作。我更新了我对这个问题的回答。我可能会加载一些虚拟数据来查看:) 测试了它,它失败了。我正在重新更新我的答案:) @JustinPihony 好的,tx 为我测试!很高兴知道那是不对的。我很欣赏它。 @pat tx 也指出了这一点。从现在开始,我会将 EXISTS 视为布尔条件【参考方案4】:您第一次使用EXISTS
似乎是正确的,但第二次似乎已关闭。您想检查机器是否最旧,但您正在检查是否存在具有相同MACHINE_ID
的机器(MIN
的使用对EXISTS
函数的结果没有影响)。
我不是数据库管理员,但考虑到子查询在某些实现中可能代价高昂,而在其他实现中,它们可能会在放置在 EXISTS
函数中时得到优化。因此,当您实际需要运行代码时,应该考虑 stringpoet 的代码......尽管正如我评论的那样,您需要 GROUP BY
所有其他字段。
还请注意,您不应使用关键字 WHERE
两次,而应使用 AND
/ OR
加入您的条件。
这是我对你和 stringpoet 代码的更正:
SELECT M.ID_MACHINE, M.NAME, MIN(M.DATE_BOUGHT)
FROM MACHINES M
WHERE NOT EXISTS (SELECT MA.*
FROM MAINTENANCE MA
WHERE MA.ID_MACHINE = M.ID_MACHINE
AND YEAR(MA.DATE) = 2011)
GROUP BY M.ID_MACHINE, M.NAME
【讨论】:
好的,太好了,这让事情变得更清楚了! tx Yuval,我将使用您的代码而不是带有 EXIST 的代码作为我的答案【参考方案5】:我认为这将是一个更好的方法。
SELECT TOP 1 MIN(A.Date_bought), A.ID_Machine, A.Name
FROM Machines A
JOIN Maintenance B on A.ID_Machine = B.ID_Machine
WHERE DATEPART(year, B.Date) != '2011'
GROUP BY A.Date_bought, A.ID_Machine, A.Name
【讨论】:
-1:OP 询问他/她的查询是否有效,而不是重写。 我认为这会失败,因为MIN
是聚合的,而您没有 GROUP BY
谢谢,错过了 GROUP BY :) 另外,对于 Jack,他还问这是否是正确的方法,我认为不是,所以我提供了另一个解决方案。当我这里只有 8 个代表时,无需投票。天哪。大声笑
嘿!这是一种更简单的方法。谢谢你,这真的很有帮助。但是,是的,我也想知道他们是否按照我的方式进行操作,主要是为了了解 EXISTS 条件是否应该像我认为的那样有效。无论如何,再次发送
我可能是错的,但我认为这不会起作用,因为 EXISTS 是一个返回布尔值的条件语句。您的 EXISTS 语句可能使您的查询实际上寻找“WHERE TRUE”或“WHERE FALSE”,这不会产生好的结果。您通常只在 IF 语句中使用 EXISTS 来查看某些内容是否存在,然后再决定如何处理它。以上是关于这是使用 EXISTS 的正确方法吗?的主要内容,如果未能解决你的问题,请参考以下文章