MySQL关于exists函数使用注意事项

exists语法

SELECT c.CustomerId, CompanyName  
FROM Customers c  
WHERE EXISTS(  
 SELECT OrderID FROM Orders o  
 WHERE o.CustomerID = cu.CustomerID)  

这里面的EXISTS是如何运作呢?子查询返回的是OrderId字段,可是外面的查询要找的是CustomerID和CompanyName字段,这两个字段肯定不在OrderID里面啊,这是如何匹配的呢?

EXISTS用于检查子查询是否至少会返回一行数据,该子查询实际上并不返回任何数据,而是返回值True或False。

EXISTS 指定一个子查询,检测行的存在。语法:EXISTS subquery。参数 subquery 是一个受限的 SELECT 语句 (不允许有 COMPUTE 子句和 INTO 关键字)。结果类型为 Boolean,如果子查询包含行,则返回 TRUE。

今天碰到一个很奇怪的问题,关于exists的,

第一个语句如下:

SELECT
    count(1)
FROM
    APPLY t
WHERE
    EXISTS (
        SELECT
            r.APPLY_ID
        FROM
            RECORD r
        WHERE
            t.APPLY_ID = r.APPLY_ID
    );

产生的结果是:89584

 

第二个语句如下:

SELECT
    count(1)
FROM
    APPLY t
WHERE
    EXISTS (
        SELECT
            max(r.FINISH_TIME)
        FROM
            RECORD r
        WHERE
            t.APPLY_ID = r.APPLY_ID
    );

产生的结果是:432382

 

确实相当奇怪,对于exist子句来说,其判断的是子查询的值是否存在,也就是说,列名,和对列名求最大值没什么区别啊。

包括MySQL官方文档中也提到

Traditionally, an EXISTS subquery starts with SELECT *, but it could begin with SELECT 5 or SELECT column1 or anything at all. MySQL ignores the SELECT list in such a subquery, so it makes no difference.
大意就是MySQL会自动忽略到SELECT的列表。

 

后来在自己的环境测试了一下,确实是MySQL的一个bug

测试环境:MySQL 5.6.31,5.7.14

mysql> create table t3(id int,t datetime);
Query OK, 0 rows affected (0.44 sec)

mysql> insert into t3 values(1,'20160812');
Query OK, 1 row affected (0.16 sec)

mysql> select 1 from dual where  exists (select id from t3 where id=2);
Empty set (0.15 sec)

mysql> select 1 from dual where  exists (select max(id) from t3 where id=2);
+---+
| 1 |
+---+
| 1 |

很明显,id等于2的列不存在,但是第二条语句还是当做TRUE来处理了。

 

也确认了下两条语句的执行计划和改写后的SQL

第一个语句

mysql> EXPLAIN EXTENDED select 1 from dual where  exists (select id from t3 where id=2);
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra            |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
|  1 | PRIMARY     | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | Impossible WHERE |
|  2 | SUBQUERY    | t3    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where      |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
2 rows in set, 2 warnings (0.00 sec)

mysql> show warnings;
+---------+------+-------------------------------------------------------------------+
| Level   | Code | Message                                                           |
+---------+------+-------------------------------------------------------------------+
| Warning | 1681 | 'EXTENDED' is deprecated and will be removed in a future release. |
| Note    | 1003 | /* select#1 */ select 1 AS `1` from DUAL  where 0                 |
+---------+------+-------------------------------------------------------------------+

 

第二个语句

mysql> EXPLAIN EXTENDED select 1 from dual where  exists (select max(id) from t3 where id=2);
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra          |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
|  1 | PRIMARY     | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | No tables used |
|  2 | SUBQUERY    | t3    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where    |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
2 rows in set, 2 warnings (0.00 sec)

mysql> show warnings;
+---------+------+-------------------------------------------------------------------+
| Level   | Code | Message                                                           |
+---------+------+-------------------------------------------------------------------+
| Warning | 1681 | 'EXTENDED' is deprecated and will be removed in a future release. |
| Note    | 1003 | /* select#1 */ select 1 AS `1` from DUAL  where 1                 |
+---------+------+-------------------------------------------------------------------+
2 rows in set (0.00 sec)

 

执行计划及改写后的SQL确实有所不同,看来,确实是MySQL的一个bug了。

于是,给官方提了个bug

http://bugs.mysql.com/bug.php?id=82562

 
总结

建议写exists语句时,子查询中直接用*,而不用对列进行任何函数操作,避免碰到官方bug,

事实上,对于abs,floor函数又没问题

mysql> select 1 from dual where  exists (select abs(id) from t3 where id=2);
Empty set (0.07 sec)

mysql> select 1 from dual where  exists (select floor(id) from t3 where id=2);
Empty set (0.00 sec)

时间: 2024-08-04 13:37:07

MySQL关于exists函数使用注意事项的相关文章

使用MySQL数据库的23个注意事项

使用MySQL,安全问题不能不注意.以下是MySQL提示的23个注意事项: 1.如果客户端和服务器端的连接需要跨越并通过不可信任的网络,那么就需要使用SSH隧道来加密该连接的通信. 2.用set password语句来修改用户的密码,三个步骤,先"mysql -u root"登陆数据库系统,然后"mysql> update mysql.user set password=password('newpwd')",最后执行"flush privilege

mysql中group_concat函数用法

一.语法   GROUP_CONCAT([DISTINCT] expr [,expr ...] [ORDER BY {unsigned_integer | col_name | expr} [ASC | DESC] [,col_name ...]] [SEPARATOR str_val])   也可以简单的理解   group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔符'])   二.group_concat

将数据从MySQL迁移到Oracle的注意事项

将数据从MySQL迁移到Oracle的注意事项 1.自动增长的数据类型处理 MYSQL有自动增长的数据类型,插入记录时不用操作此字段,会自动获得数据值.ORACLE没有自动增长的数据类型,需要建立一个自动增长的序列号,插入记录时要把序列号的下一个值赋于此字段. CREATE SEQUENCE 序列号的名称 (最好是表名+序列号标记) INCREMENT BY 1 START WITH 1 MAXVALUE 99999 CYCLE NOCACHE; INSERT 语句插入这个字段值为: 序列号的名

MySQL 存储过程和函数(转)

概述 一提到存储过程可能就会引出另一个话题就是存储过程的优缺点,这里也不做讨论,一般别人问我我就这样回答你觉得它好你就用它.因为mysql中存储过程和函数的语法非常接近所以就放在一起,主要区别就是函数必须有返回值(return),并且函数的参数只有IN类型而存储过程有IN.OUT.INOUT这三种类型. 语法 创建存储过程和函数语法 CREATE PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine_bod

MySQL关于exists的一个bug_Mysql

今天碰到一个关于exists很奇怪的问题 第一个语句如下: SELECT count(1) FROM APPLY t WHERE EXISTS ( SELECT r.APPLY_ID FROM RECORD r WHERE t.APPLY_ID = r.APPLY_ID ); 产生的结果是:89584 第二个语句如下: SELECT count(1) FROM APPLY t WHERE EXISTS ( SELECT max(r.FINISH_TIME) FROM RECORD r WHERE

MySql存储过程与函数详解

存储过程和函数是在数据库中定义一些SQL语句的集合,然后直接调用这些存储过程和函数来执行已经定义好的SQL语句.存储过程和函数可以避免开发人员重复的编写相同的SQL语句.而且,存储过程和函数是在MySQL服务器中存储和执行的,可以减少客户端和服务器端的数据传输.一.存储过程1.1.基本语法 CREATE PROCEDURE sp_name ([proc_parameter[,...]])    [characteristic ...] routine_body   Sp_name:存储过程的名称

理解MySQL存储过程和函数_Mysql

一.概述  一提到存储过程可能就会引出另一个话题就是存储过程的优缺点,这里也不做讨论,一般别人问我我就这样回答你觉得它好你就用它.因为mysql中存储过程和函数的语法非常接近所以就放在一起,主要区别就是函数必须有返回值(return),并且函数的参数只有IN类型而存储过程有IN.OUT.INOUT这三种类型. 二.语法   创建存储过程和函数语法 CREATE PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] rout

mysql的字符串函数

mysql|函数|字符串 注意:如果结果的长度大于服务器参数max_allowed_packet,字符串值函数返回NULL 对于针对字符串位置的操作,第一个位置被标记为1. ASCII(str) 返回字符串str的最左面字符的ASCII代码值.如果str是空字符串,返回0.如果str是NULL,返回NULL. mysql> select ASCII('2');         -> 50 mysql> select ASCII(2);         -> 50 mysql>

如何为MySQL添加新函数

有两个途径来为MySQL添加新函数: 你可以通过自行医函数接口 (UDF)来添加函数.自定义函数被编译为目标文件,然后用CREATE FUNCTION 和DROP FUNCTION 声明动态地添入到服务器中及从服务器中移出. 你可以将函数添加为MySQL固有(内建)函数.固有函数被编译进mysqld服务器中,成为永久可用的. 每种途径都有其优点和缺点: 如果你编写自定义函数,你除了安装服务器本身之外还要安装目标文件.如果将你的函数编译进服务器中,你就不需要这么做了. 你可以给二进制版本的MySQ