Maison  >  Article  >  base de données  >  Méthode de conversion implicite dans MySQL

Méthode de conversion implicite dans MySQL

一个新手
一个新手original
2017-10-13 10:07:081152parcourir

隐式转化规则

官方文档中关于隐式转化的规则是如下描述的:

Si un ou les deux arguments sont NULL, le résultat de la comparaison est NULL, à l'exception du 96b4fef55684b9312718d5de63fb7121 opérateur de comparaison d’égalité. Pour NULL 96b4fef55684b9312718d5de63fb7121 NULL, le résultat est vrai. Aucune conversion n'est nécessaire.

  • Si les deux arguments d'une opération de comparaison sont des chaînes, ils sont comparés en tant que chaînes.

  • Si les deux arguments sont des entiers , ils sont comparés comme des entiers.

  • Les valeurs hexadécimales sont traitées comme des chaînes binaires si elles ne sont pas comparées à un nombre.

  • Si l'un des arguments est une colonne TIMESTAMP ou DATETIME et l'autre argument est une constante, la constante est convertie en horodatage avant que la comparaison ne soit effectuée. Ceci est fait pour être plus convivial pour ODBC. Notez que cela n’est pas fait pour les arguments de IN() ! Pour être sûr, utilisez toujours des chaînes datetime, date ou heure complètes lorsque vous effectuez des comparaisons. Par exemple, pour obtenir de meilleurs résultats lors de l'utilisation de BETWEEN avec des valeurs de date ou d'heure, utilisez CAST() pour convertir explicitement les valeurs dans le type de données souhaité.
    Une sous-requête sur une seule ligne d'une ou plusieurs tables n'est pas considérée comme une constante. Par exemple, si une sous-requête renvoie un entier à comparer à une valeur DATETIME, la comparaison est effectuée sous la forme de deux entiers. L'entier n'est pas converti en valeur temporelle. Pour comparer les opérandes en tant que valeurs DATETIME, utilisez CAST() pour convertir explicitement la valeur de la sous-requête en DATETIME.

  • Si l'un des arguments est une valeur décimale, la comparaison dépend de l'autre argument. Les arguments sont comparés sous forme de valeurs décimales si l'autre argument est une valeur décimale ou entière, ou sous forme de valeurs à virgule flottante si l'autre argument est une valeur à virgule flottante.

  • Dans tous les autres Dans certains cas, les arguments sont comparés sous forme de nombres à virgule flottante (réels). 96b4fef55684b9312718d5de63fb7121 对两个 NULL 做比较时会返回 1,这两种情况都不需要做类型转换

两个参数都是字符串,会按照字符串来比较,不做类型转换

    1. 两个参数都是整数,按照整数来比较,不做类型转换

    2. 有一个参数是TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为 timestamp

    3. 有一个参数是 décimal类型,如果另外一个参数是 décimal 或者整数,会将整数转换为 décimal 后进行比较,如果另外一个参数是浮点数,则会把 转换为浮点数进行比较

    4. 所有其他情况下,两个参数都会被转换为浮点数再进行比较

    5. 问题描述

    6. où 条件语句里,字段属性和赋给的条件,当数据类型不一样,这时候是没法直接比较的,需要进行一致转换

      默认转换规则是:
    7. 不同类型全都转换为浮点型(下文都说成整型了,一个意思)

如果字段是字符,条件是整型,那么会把表中字段全都转换为整型(也就是上面图中的问题,下面有详细解释)

  • 转换总结

  • 字符转整型

    • 字符开头的一律为0
    数字开头的,直接截取到第一个不是字符的位置

  1. 时间类型转换

  • 按照字符串进行截取

  • 23:12:13 -> 2023-12-13(这个后文有讨论)
  • 对于不符合的时间值,如10:12:32等,会变为 0000-00-00或为 空

  • cast函数只能转datetime,不能转timestamp
    • 如果按照timestamp来理解,因为timestamp是有范围的('1970-01-01 00:00:01.000000' to'2038-01-19 03:14:07.999999'),所以只能是2023年,而不能是1923年

    • 直接截取heure字段
      直接截取date字段
    • 无意义,直接为 00:00:00

    • Ajouter 00:00:00

    • date à dateheure ou horodatage

    • date à heure

    • dateheure ou horodatage à ce jour

    • dateheure ou horodatage à heure

    • heure en dateheure ou horodatage

    • Lorsque l'heure et la dateheure sont converties en nombres, elles deviendront double précision, plus ms (différentes versions)

    Analyse de cas

    • Structure de table, le champ de nom a un index

    -- 注意name字段是有索引的CREATE TABLE `t3` (  `id` int(11) NOT NULL,  `c1` int(11) NOT NULL,  `name` varchar(100) NOT NULL DEFAULT 'fajlfjalfka',  KEY `name` (`name`),  KEY `id` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin11 row in set (0.00 sec)
    -- 模拟线上一个隐式转换带来的全表扫面慢查询-- 发生隐式转换
    xxxx.test> select * from t3 where name = 0;
    +----+----+-------------+
    | id | c1 | name        |
    +----+----+-------------+
    |  1 |  2 | fajlfjalfka |
    |  2 |  0 | fajlfjalfka |
    |  1 |  2 | fajlfjalfka |
    |  2 |  0 | fajlfjalfka |
    +----+----+-------------+
    4 rows in set, 4 warnings (0.00 sec)-- 上述SQL执行计划是全表扫描,扫描后,字符转整型,都是0,匹配上了条件,全部返回
    xxxx.test> desc select * from t3 where name = 0;
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    |  1 | SIMPLE      | t3    | ALL  | name          | NULL | NULL    | NULL |    4 | Using where |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    1 row in set (0.00 sec)-- 加上单引号后,是走name索引的,非全表扫描
    xxxx.test> desc select * from t3 where name = '0';
    +----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------+
    | id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra                 |
    +----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------+
    |  1 | SIMPLE      | t3    | ref  | name          | name | 102     | const |    1 | Using index condition |
    +----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------+
    1 row in set (0.00 sec)-- 走索引,没返回
    xxxx.test>  select * from t3 where name = '1';
    Empty set (0.00 sec)

    Explication

    • Si la condition est écrite comme 0 ou 1, la table entière sera analysée. Tous les champs de nom doivent être convertis de caractères en entiers, puis comparés à 0 ou 1. Puisqu'il s'agit tous de caractères commençant par une lettre, ils seront tous convertis en 0 et le résultat renvoyé correspond à toutes les lignes.

    • Ensuite, quelqu'un a demandé, pourquoi ne pas changer automatiquement le 0 dans la condition en '0' ? voir ci-dessous.

    Exemple de conversion

    -- 字符开头,直接是0
    xxxx.test> select cast('a1' as unsigned int) as test ;
    +------+
    | test |
    +------+
    |    0 |
    +------+
    1 row in set, 1 warning (0.00 sec)
    
    xxxx.test> show warnings;
    +---------+------+-----------------------------------------+
    | Level   | Code | Message                                 |
    +---------+------+-----------------------------------------+
    | Warning | 1292 | Truncated incorrect INTEGER value: 'a1' |
    +---------+------+-----------------------------------------+
    1 row in set (0.00 sec)-- 开头不是字符,一直截取到第一个不是字符的位置
    xxxx.test> select cast('1a1' as unsigned int) as test ; 
    +------+
    | test |
    +------+
    |    1 |
    +------+
    1 row in set, 1 warning (0.00 sec)
    
    xxxx.test> select cast('123a1' as unsigned int) as test ;
    +------+
    | test |
    +------+
    |  123 |
    +------+
    1 row in set, 1 warning (0.00 sec)-- 直接按照字符截取,补上了20(不能补19)
    xxxx.test> select cast('23:12:13' as datetime) as test ;
    +---------------------+
    | test                |
    +---------------------+
    | 2023-12-13 00:00:00 |
    +---------------------+
    1 row in set (0.00 sec)-- 为什么不能转换为timestamp,没搞清楚,官方文档给的转换类型里没有timestamp。如果是这样的话,上面的datetime就不好解释为什不是1923了。难道是检测了当前的系统时间?
    xxxx.test> select cast('23:12:13' as timestamp) as test ;    
    ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'timestamp) as test' at line 1-- 这个时间无法转换成datetime
    xxxx.test> select cast('10:12:32' as datetime) as test ;         
    +------+
    | test |
    +------+
    | NULL |
    +------+
    1 row in set, 1 warning (0.00 sec)
    
    xxxx.test> show warnings ;
    +---------+------+--------------------------------------+
    | Level   | Code | Message                              |
    +---------+------+--------------------------------------+
    | Warning | 1292 | Incorrect datetime value: '10:12:32' |
    +---------+------+--------------------------------------+
    1 row in set (0.00 sec)-- 5.5版本下,时间转字符,会增加ms
    xxxx.(none)> select version();
    +------------+
    | version()  |
    +------------+
    | 5.5.31-log |
    +------------+
    1 row in set (0.00 sec)
    
    xxxx.(none)> select CURTIME(), CURTIME()+0, NOW(), NOW()+0 ;
    +-----------+---------------+---------------------+-----------------------+
    | CURTIME() | CURTIME()+0   | NOW()               | NOW()+0               |
    +-----------+---------------+---------------------+-----------------------+
    | 15:40:01  | 154001.000000 | 2016-05-06 15:40:01 | 20160506154001.000000 |
    +-----------+---------------+---------------------+-----------------------+
    1 row in set (0.00 sec)-- 5.6 不会
    xxxx.test> select version();
    +------------+
    | version()  |
    +------------+
    | 5.6.24-log |
    +------------+
    1 row in set (0.00 sec)
    
    xxxx.test> select CURTIME(), CURTIME()+0, NOW(), NOW()+0 ;
    +-----------+-------------+---------------------+----------------+
    | CURTIME() | CURTIME()+0 | NOW()               | NOW()+0        |
    +-----------+-------------+---------------------+----------------+
    | 15:40:55  |      154055 | 2016-05-06 15:40:55 | 20160506154055 |
    +-----------+-------------+---------------------+----------------+
    1 row in set (0.00 sec)

    Pourquoi ne pas convertir le where name = 0 dans 0 en '0' ?

    • Si vous convertissez des nombres en caractères, tels que 0 en « 0 », le résultat de la requête ne peut être que que le champ est égal à « 0 », mais en fait, le données dans le tableau, par exemple, « a0 », « 00 », ce sont en fait les 0 souhaités par l'utilisateur. Après tout, l'utilisateur a spécifié le nombre 0, donc MySQL prend toujours la demande de l'utilisateur comme standard. 00' ne sera pas restitué à l'utilisateur.

    Résumé

    • Avec le contenu ci-dessus, la question du début peut être expliquée.

    • L'exemple ci-dessus peut-il être utilisé pour contourner l'authentification ?

    Supplément

    -- 上面遗留的问题,跟系统时间并没有关系。怀疑虽然指定的是datetime,但是内部还是按照timestamp去做的。
    mysql> select now();
    +---------------------+
    | now()               |
    +---------------------+
    | 1999-08-03 14:16:50 |
    +---------------------+
    1 row in set (0.00 sec)
    
    mysql> select cast('23:12:13' as datetime) as test ;
    +---------------------+
    | test                |
    +---------------------+
    | 2023-12-13 00:00:00 |
    +---------------------+
    1 row in set (0.00 sec)


    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    Déclaration:
    Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn