8

这个问题存在于 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。

我错了吗?

4

3 回答 3

8

更新:进一步分析和展开 MySQL 的> ALL奇怪实现。这个答案应该被认为是 MySQL 特定的。因此,为了进一步免责声明,此处对答案的解释> ALL不适用于其他 RDBMS(除非有其他 RDBMS 复制了 MySQL 实现)。> ALL从到构造的内部转换MAX,仅适用于 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

这就是为什么它同时返回102从表 t1


应用 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;

最后两个查询返回10and 2,而前两个查询返回空集

现场测试: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)

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

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)
于 2012-07-13T01:39:02.237 回答
5

这是 MySQL 中的一个错误(在此处报告和验证)。

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

不同于 MySQL 文档中规定的行为和 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

因此不应返回此。请参阅明确规定的 ANSI 规范

" " 的结果R <comp op> <quantifier> T是通过将隐含的<comparison predicate>" R <comp op> RT" 应用于 中的每一行得出RTT

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.
于 2012-07-15T14:19:08.313 回答
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

删除段落修正ALL:我们不应该仅仅因为 MySQL 的实现有缺陷而厌恶;-)

在 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);

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

-- 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 |


此外,在所有 RDBMS中MAX一致的:

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 值的见解被排除在外。为什么?

MySQL 的ALL问题仅限于 MySQL,非常不一致。MySQL在其 WHERE 子句中转换ALL为;MAX而在其 SELECT 子句中,MySQL 转换ALL链式AND。

其他 RDBMS 实现> ALL链式AND,ALL 表达式上的 NULL 规则适用于它们的 SELECT 子句和 WHERE 子句,它们在 SELECT 子句和 WHERE 子句上具有一致的结果。他们的 on NULLon规则ALL是符合 ANSQL SQL 的

于 2012-07-13T02:33:12.213 回答