有趣的观察结果中的SQL WHERE EXISTS

时间:2012-01-13 05:12:21

标签: sql select subquery where exists

我想知道是否有人可以解释为什么会发生以下情况:

我有下面的表格。它有2列“A”和“B”:

A       B
==========
1,      11
2,      12
3,      13
4,      14
5,      15
6,      16
7,      17
8,      18
9,      19

如果我运行以下查询: -

SELECT * FROM 
    (SELECT A,B FROM Table_1) T1
WHERE EXISTS 
    (SELECT 'X' FROM Table_1 WHERE A = 3)

我拿到整张桌子。我理解这是因为EXISTS子句检查它是否在语句中找到了一行

但是,如果我运行以下查询,我只获得表的一部分

SELECT * FROM 
    (SELECT A,B FROM Table_1) T1
WHERE EXISTS 
    (SELECT 'X' FROM Table_1 WHERE T1.A=3 OR T1.A=4)

部分结果如下

A     B
========
3,    13
4,    14

有人可以解释为什么会这样吗?

5 个答案:

答案 0 :(得分:3)

让我们分解您实际要求数据库服务器执行的操作。

SELECT * FROM - 获取所有字段。我从现在开始会忽略这一点,因为它并不重要。

(SELECT A,B FROM Table_1) T1 - 从表中获取所有行,并命名该结果集" T1"。

WHERE EXISTS (SELECT 'X' FROM Table_1 WHERE A = 3) - 从表格中选择上述所有行的独立性。这种情况在表中每行发生一次 - 但总是做同样的事情,因为子查询中没有使用T1。如果表中的一行具有A = 3(总是如此),则不做任何限制。否则,丢弃特定的T1(这不会发生在这里)。

WHERE EXISTS (SELECT 'X' FROM Table_1 WHERE T1.A=3 OR T1.A=4) - 这个很棘手。您从表中选择了所有行的第二个选项,但是您要严格限制此条件:T1.A=3 OR T1.A=4。此条件基于T1的结果 - 但这不是整个表格,只是特定行。当你说SELECT * FROM mytable WHERE mytable.A=3时,你并不是指"如果某行有A = 3"则选择mytable的所有行,你只想选择那些真实的特定行。因此,子查询SELECT 'X' FROM Table_1 WHERE T1.A=3 OR T1.A=4不包含任何行或Table_1中的所有行,具体取决于T1.A中的值。因为您使用EXISTS,所以所有行都为true,而对于所有行都为false。

这就是为什么你会得到不同的结果 - 你的子查询在Table_1中每行执行一次。在第一种情况下,它总是包含一行。在第二种情况下,它包含无行或全部九个,具体取决于Table_1中包含的T1的特定行。

答案 1 :(得分:1)

如果删除一些子查询,可能会更容易。您的查询基本上归结为以下内容:

查询1:从Table_1中选择true的所有记录。这是因为A中的Table_1个字段包含3个或4个。

查询2:从Table_1中选择A等于3或A等于4的所有记录

在SQL中表示,您的查询可以简化为

SELECT * FROM Table_1

SELECT * FROM Table_1
WHERE A IN (3,4)

答案 2 :(得分:1)

我相信它会检查T1的当前行,看看A = 3还是A = 4,类似于你写的:

SELECT * FROM 
  (SELECT A,B FROM Table_1) T1
WHERE A=3 OR A=4;

答案 3 :(得分:1)

哇,很酷。

你有一些额外的SQL。你真正在做的是:

 SELECT * FROM Table_1 T1 WHERE EXISTS 
    (SELECT 'X' FROM Table_1 WHERE T1.A = 3 OR T1.A = 4)

那发生了什么事?

当您将Table_1的每一行读为T1时,引擎会评估子选择,以确定T1中的行是否应包含在结果集中。

在第一行,其中T1.A为1,则T1.A = 3为FALSE,T1.A = 4为FALSE,因此子选择失败。该行不包含在结果集中。

当A = 2时相同。不包括A = 2的行。

但是对于接下来的两行(3和4的T1.A),OR的至少一侧评估为TRUE并且子选择成功。所以包括这两行。

当你点击T1.A为5时,对于表的其余部分,子选择失败。

答案 4 :(得分:1)

在SQL中总是有多种方法可以编写相同的东西。例如这个

SELECT * FROM 
    (SELECT A,B FROM Table_1) T1

......可以改写为:

SELECT A, B
  FROM Table_1

后者更简单,我认为没有理由更喜欢前者。相应地重写您的第一个查询

SELECT A, B
  FROM Table_1
 WHERE EXISTS (
               SELECT 'X' 
                 FROM Table_1 
                WHERE A = 3
              );

我删除了相关名T1,因为它没有用处。子查询不引用其“外部”表表达式,因此不需要消除每个外观Table_1的歧义。

我想你明白这里发生了什么:如果Table_1中的一行或多行满足搜索条件A = 3,则返回整个表,否则返回空集。虽然它是一个有效的查询,但它通常不是一个非常有用的结构。

但是,对于第二个查询,至少需要一个相关名,因为子查询确实引用了其外表:

SELECT A, B
  FROM Table_1 T1
 WHERE EXISTS (
               SELECT 'X' 
                 FROM Table_1 T2
                WHERE T1.A IN (3, 4)
              );

同样,这在语义上等同于您的第二个查询。请注意,我在子查询中给出了Table_1的外观相关名T2,但T2没有出现在子查询的WHERE子句中。由于未使用T2,我们可以完全删除子查询(因此需要相关名称):

SELECT A, B
  FROM Table_1
 WHERE A IN (3, 4);

值得指出的是,子查询引用“外部”表表达式的能力通常被用作“相关子查询”,即搜索条件(WHERE子句)涉及“内部”和“外部” '表(您的第二个查询的搜索条件仅涉及'外部'表)。

使用usual parts and suppliers database,这是一个相关子查询示例,用于实现semijoin查找供应商(S)至少有一部分的供应商(SP) :

SELECT SNO, SNAME
  FROM S
 WHERE EXISTS (
               SELECT *
                 FROM SP
                WHERE SP.SNO = S.SNO
              );

请注意,子查询的搜索条件将“外部”表SP与“内部”表S相关联。此外,“外部”表格中的投影SELECT SNO, SNAME不需要包含相关名称S,因为“外部”表格中的SP不在“内部”的范围内表