奇怪的“所有”在“哪里”

时间:2012-07-13 00:52:12

标签: mysql sql

MySQL的最新版本存在问题,所以我甚至怀疑这可能是一个错误。

以下是两个表格:

t1(id int), values (10),(2)
t2(id int), values (0),(null),(1)

执行:

select id from t1 where id > all (select id from t2);

返回结果集:

+------+
| id   |
+------+
|   10 |
|    2 |
+------+

根据我的知识和页面http://dev.mysql.com/doc/refman/5.5/en/all-subqueries.html

该语句应返回空结果!因为“where”中的每个判断都会导致null,如下所示:

select id > all (select id from t2)  as c1 from t1;

返回:

+------+
| c1   |
+------+
| NULL |
| NULL |
+------+

实际上select id from t1 where null;没有返回任何内容!

最后,我尝试了这个:

explain extended select id from t1 where id > all (select id from t2);
show warnings;
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                                             |
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------+
| Note  | 1003 | select `test`.`t1`.`id` AS `id` from `test`.`t1` where <not>((`test`.`t1`.`id` <= (select max(`test`.`t2`.`id`) from `test`.`t2`))) |
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------+

1行(0.00秒)

我们可以看到MySQL优化了原始SQL,这实际上符合结果集。

但我不认为优化的SQL等于原始的SQL。

我错了吗?

3 个答案:

答案 0 :(得分:8)

更新:进一步分析和展开MySQL的> ALL奇数实现。这个答案应该被视为特定于MySQL。因此,对于进一步的免责声明,有关> ALL的答案的解释不适用于其他RDBMS(除非有其他RDBMS复制了MySQL实现)。从> ALLMAX构造的内部转换仅适用于MySQL。

此:

select id from t1 where id > all (select id from t2); 

在语义上等同于:

select id from t1 where id > (select max(id) from t2); 

由于select max(id) from t2返回1,因此第二个查询具体化为:

select id from t1 where id > 1

这就是它从表t1

返回102的原因

正在应用NULL规则的一个实例是当您使用NOT IN时的一个示例:

DDL:

create table t1(id int);

insert into t1 values (10),(2);


create table t2(id int); 

insert into t2 values (0),(null),(1);

查询:

select * from t1 where id not in (select id from t2);

-- above is evaluated same as the following query, so the rules about null applies,
-- hence the above and following query will not return any record.    

select * from t1 where id <> 0 and id <> null and id <> 1;



-- to eliminate null side-effect, do this:
select * from t1 where id not in (select id from t2 where id is not null);

-- which is equivalent to this:
select * from t1 where id <> 0 and id <> 1;

最后两个查询返回102,而前两个查询返回空集

实时测试:http://www.sqlfiddle.com/#!2/82865/1

希望这些示例能够消除您对NULL规则的困惑。


关于

  

但我不认为优化的sql等于原始的。

优化的sql是这样的:

select `test`.`t1`.`id` AS `id` from `test`.`t1` where <not>((`
test`.`t1`.`id` <= (select max(`test`.`t2`.`id`) from `test`.`t2`)))

这实际上与原始查询相同:select id from t1 where id > all (select id from t2);

构造t1.field > all (select t2.field from t2)只是一个语法糖:

t1.field > (select max(t2.field) from t2)

如果您将在MySql的优化SQL上应用DeMorgan定理:

not (t1.id <= (select max(t2.id) from t2))

这相当于:

t1.id > (select max(t2.id) from t2)

反过来相当于句法糖ALL

t1.id > ALL(select t2.id from t2)

答案 1 :(得分:5)

这是MySQL中的一个错误(Reported and verified here)。

  

该修复程序将在5.6.7(下一个5.6x版本)以及   下一个主要树(5.7x)

differs from the stated behaviour in the MySQL docs和ANSI标准中规定的。

此外,它在MySQL中甚至不一致,当子查询引用表时,与子查询包含(相同)文字值时相比,您得到的结果不同。

CREATE TABLE t2
  (
     id INT
  );

INSERT INTO t2
VALUES      (0),
            (NULL),
            (1);

/*Returns row with 10*/
SELECT *
FROM   (SELECT 10 AS id) T
WHERE  id > ALL (SELECT id
                 FROM   t2);

/*Returns no rows. Explain Plan says "Impossible Where"*/
SELECT *
FROM   (SELECT 10 AS id) T
WHERE  id > ALL (SELECT 0
                 UNION ALL
                 SELECT NULL
                 UNION ALL
                 SELECT 1); 

根据规范,第二种行为是正确的。 10 > ALL( (0),(null),(1) )如何按逻辑评估如下

10 > 0 =  TRUE
10 > NULL =  UNKNOWN
10 > 1 =  TRUE

根据三值逻辑的规则

TRUE AND UNKNOWN AND TRUE = UNKNOWN

因此,此行应返回。 See the ANSI specification明确说明

  

R <comp op> <quantifier> T”的结果是由...导出的   应用隐含的<comparison predicate>R <comp op> RT”   RT中的每一行T

因此,当T为Nullable时,这不是语义上有效的优化。规范的完整部分转载如下。

  

8.7

     Function

     Specify a quantified comparison.

     Format

     <quantified comparison predicate> ::=
          <row value constructor> <comp op> <quantifier> <table subquery>


     <quantifier> ::= <all> | <some>

     <all> ::= ALL

     <some> ::= SOME | ANY


     Syntax Rules

     1) The <row value constructor> shall be of the same degree as the
        result of the <table subquery>.

     2) The data types of the values of the <row value constructor>
        shall be respectively comparable to those of the columns of the
        <table subquery>.

     3) The collating sequence for each pair of respective values in
        the <quantified comparison predicate> is determined in the same
        manner as described in Subclause 8.2, "<comparison predicate>".

     Access Rules

        None.

     General Rules

     1) Let R be the result of the <row value constructor> and let T be
        the result of the <table subquery>.

     2) The result of "R <comp op> <quantifier> T" is derived by the
        application of the implied <comparison predicate> "R <comp op>
        RT" to every row RT in T:

        Case:

        a) If T is empty or if the implied <comparison predicate> is
          true for every row RT in T, then "R <comp op> <all> T" is
          true.

        b) If the implied <comparison predicate> is false for at least
          one row RT in T, then "R <comp op> <all> T" is false.

        c) If the implied <comparison predicate> is true for at least
          one row RT in T, then "R <comp op> <some> T" is true.

        d) If T is empty or if the implied <comparison predicate> is
          false for every row RT in T, then "R <comp op> <some> T" is
          false.

        e) If "R <comp op> <quantifier> T" is neither true nor false,
          then it is unknown.

答案 2 :(得分:4)

更新(2012-07-15)问题仅限于MySQL,也许我在浏览Chrome上的许多sqlfiddle选项卡时感到困惑。 Postgresql没有问题,它的NULL行为在SELECT和WHERE子句上都是一致的,与Sql Server相同。

很明显,我和你一样困惑,我在Sql Server上尝试了你的MySql查询:http://www.sqlfiddle.com/#!3/82865/6

-- query 1
select 
    case when id > all(select id from t2) then 1 else 0 end as c1
from t1;


-- query 2
select 
    *
from t1
where id > all(select id from t2);

第一个查询将0返回给所有行。

| C1 |
------
|  0 |
|  0 |

当然,第二个查询(具有WHERE子句)不应返回任何行。哪个Sql Server正当地做了。 虽然我不同意0作为列C1(应该是1的结果,但我赞赏Sql Server保持一致。

然后在MySql(http://www.sqlfiddle.com/#!2/82865/25)和Postgresql(http://www.sqlfiddle.com/#!1/82865/5)上的MySql查询中:

-- query 1
select 
    id > all(select id from t2) as c1
from t1;


-- query 2
select 
    *
from t1
where id > all(select id from t2);

MySql和Postgresql都产生了相同的输出:

MySql产生此输出:

|     C1 |
----------
| (null) |
| (null) |


| ID |
------
| 10 |
|  2 |

我认为第二个查询具有正确的输出,但SELECT子句(第一个查询)的嵌入条件另有说明。我不喜欢这种不一致。

严格段落修正:MySQL有问题。 Postgresql实现是正确的,它的SELECT子句和WHERE子句产生相同的结果,它在SELECT上返回NULL,并在WHERE子句上返回空行。

现在,我想在 Postgresql或 MySql论坛上提出这个问题,为什么WHERE子句的条件和SELECT子句中嵌入的条件之间的结果存在差异。

我希望stackoverflow中有一个相似的灵魂可以为我们进一步解释这个不一致: - )


<击> 无论合成糖ALL多么甜,我不再倾向于使用它了。它在WHERE子句和嵌入在SELECT上之间的结果不一致。无论如何,我在所有查询中使用MAX,恕我直言,意图比英语ALL更清晰,我需要继续使用MAX的原因越多:

严格的段落修正:我们不应该因为MySQL的实施存在缺陷而对ALL产生厌恶; - )

在MySql和Postgresql上,MAX产生相同的输出

-- Query 1
select 
    id > all(select id from t2) as c1,
    id > (select max(id) from t2) as c2
from t1;


-- Query 2
select 
    *
from t1
where id > all(select id from t2);


-- Query 3
select 
    *
from t1
where id > (select max(id) from t2);

MAX输出在两个RDBMS上都是一致的。

-- Query 1 output:

|     C1 | C2 |
---------------
| (null) |  1 |
| (null) |  1 |



-- Query 2 output:

MySql return this:

| ID |
------
| 10 |
|  2 |

Postgresql return empty row. Which is correct



-- Query 3 output:

| ID |
------
| 10 |
|  2 |


此外,MAX在所有RDBMS中一致

select 
    case when id > all(select id from t2) then 'Yes' else 'Oh No!' end as c1,
    case when id > (select max(id) from t2) then 'Yes' else 'Oh No!' end as c2
from t1;


select 
    *
from t1
where id > all(select id from t2);


select 
    *
from t1
where id > (select max(id) from t2);

现场测试:



为了确定它,只有MySQL实现id > ALLid > (SELECT MAX。 Postgresql和Sql Server都将id > ALL(0,NULL,1)解释为id > 0 AND id > NULL AND id > 1,因此Postgresql和Sql Server都会产生相同的输出。

关于NULL规则的启示来自Martin Smith对NULL values are excluded. Why?

的见解

MySQL的ALL问题仅与MySQL隔离,它非常不一致。 MySQL在其WHERE子句上将ALL翻译为MAX;在其SELECT子句中,MySQL将ALL转换为链接 AND。

其他RDBMS实现> ALL作为链接 ANDs,ALL表达式上的NULL规则适用于它们的SELECT子句和WHERE子句,它们对SELECT子句和WHERE子句都有一致的结果。他们在NULL ALL上的规则是符合ANSQL SQL的