MySQL의 암시적 변환 방법

一个新手
풀어 주다: 2017-10-13 10:07:08
원래의
1173명이 탐색했습니다.

隐式转化规则

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

인수 중 하나 이상이 NULL이면 비교 결과는 NULL입니다. 단, NULL 안전 < ; => 동등 비교 연산자. NULL <=> NULL, 결과는 true입니다. 변환이 필요하지 않습니다.

  • 비교 연산의 두 인수가 모두 문자열이면 문자열로 비교됩니다.

  • 두 인수가 모두 정수이면 정수로 비교됩니다.

  • 16진수 값은 다음과 같이 처리됩니다. 숫자와 비교되지 않은 경우 이진 문자열입니다.

  • 인수 중 하나가 TIMESTAMP 또는 DATETIME 열이고 다른 인수가 상수인 경우 비교가 수행되기 전에 상수가 타임스탬프로 변환됩니다. 이는 보다 ODBC 친화적이도록 수행됩니다. IN()에 대한 인수에 대해서는 이 작업이 수행되지 않습니다. 안전을 위해 비교를 수행할 때 항상 완전한 날짜/시간, 날짜 또는 시간 문자열을 사용하십시오. 예를 들어 날짜 또는 시간 값과 함께 BETWEEN을 사용할 때 최상의 결과를 얻으려면 CAST()를 사용하여 값을 원하는 데이터 유형으로 명시적으로 변환합니다.
    테이블의 단일 행 하위 쿼리는 상수로 간주되지 않습니다. 예를 들어 하위 쿼리가 DATETIME 값과 비교할 정수를 반환하는 경우 비교는 두 개의 정수로 수행됩니다. 정수는 임시 값으로 변환되지 않습니다. 피연산자를 DATETIME 값으로 비교하려면 CAST()를 사용하여 하위 쿼리 값을 DATETIME으로 명시적으로 변환합니다.

  • 인수 중 하나가 10진수 값인 경우 비교는 다른 인수에 따라 달라집니다. 다른 인수가 10진수 또는 정수 값이면 인수는 10진수 값으로 비교되고, 다른 인수가 부동 소수점 값이면 부동 소수점 값으로 비교됩니다.

  • 다른 모든 경우에는 인수가 부동 소수점 값으로 비교됩니다. -포인트(실수) 숫자.

翻译为中文就是:

    1. 两个参数至少有一个是 NULL 时,比较的结也是 NULL, 例외是使용 <=> 对两个 NULL 做比较时会返回 1,这两种情况都不需要做类型转换

    2. 两个参数duc是字符串,会按 사진자符串来比较,不做类型转换

    3. ㅋㅋㅋ时,会被当做 2륜 제조串

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

    5. 有一个参数是 십진수 类型,如果另외일个参数是 십진수 或者整数,会将整数转换为 십진수 后进行比较,如果另外一个参数是浮点数,则会把 십진수 转换为浮点数进行比较

    6. 所有其他情况个参数city会被转换为浮点数再进行比较

    7. 问题描述

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

默认转换规则是:

  • 不同类型전체도시转换为浮点型(下文都说成整型了,一个意思)
  • 如果字段是字符,条件是整型,那么会把表中字段전체도시转换为整型(也就是上面图中的问题,下면有详细解释)
    • 转换总结
    • 字符转整型

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

  • 时间类型转换

  • 按字符串进行截取
  • 23: 12:13 -> 2023-12-13(这个后文有讨论)
    • 对于不符值,如10:12:32等,会变为 0000-00-00 或为 공간

    • 캐스트函数只能转datetime,不能转timestamp
    • 如果按Phototimestamp来理解,因为timestamp是有范围的('1970-01-01 00:00:01.000000' to'2038-01-19 0 3:14:07.999999' ),所以只能是2023年,而不能是1923年
    • 直接截取time字段
      直接截取date字段
      无意义,直接为 00:00:00
    • 00:00:00

    • 날짜를 날짜/시간 또는 타임스탬프에 추가

    • 날짜를 시간

    • datetime 또는 타임스탬프를 날짜까지

    • datetime 또는 타임스탬프 - 시간

    • time to datetime 또는 timestamp

    • time과 datetime이 숫자로 변환되면 배정밀도에 ms(다른 버전)가 추가됩니다.

    사례 분석

    • 테이블 구조, 이름 필드에 인덱스가 있음

    -- 注意name字段是有索引的CREATE TABLE `t3` (  `id` int(11) NOT NULL,  `c1` int(11) NOT NULL,  `name` varchar(100) NOT NULL DEFAULT &#39;fajlfjalfka&#39;,  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 = &#39;0&#39;;
    +----+-------------+-------+------+---------------+------+---------+-------+------+-----------------------+
    | 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 = &#39;1&#39;;
    Empty set (0.00 sec)
    로그인 후 복사

    Explanation

    • 조건이 0이나 1로 쓰여지면 모든 이름 필드를 문자에서 정수로 변환한 후 0이나 1과 비교해야 합니다. 모두 문자로 시작하는 문자이므로 모두 0으로 변환되어 반환되는 결과는 모두 행이다.

    • 그런데 누군가가 조건의 0'0'으로 자동으로 변경하면 어떨까요? 아래를 참조하세요. 0 自动改成 &#39;0&#39; ?见下文。

    转换举例

    -- 字符开头,直接是0
    xxxx.test> select cast(&#39;a1&#39; 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: &#39;a1&#39; |
    +---------+------+-----------------------------------------+
    1 row in set (0.00 sec)-- 开头不是字符,一直截取到第一个不是字符的位置
    xxxx.test> select cast(&#39;1a1&#39; as unsigned int) as test ; 
    +------+
    | test |
    +------+
    |    1 |
    +------+
    1 row in set, 1 warning (0.00 sec)
    
    xxxx.test> select cast(&#39;123a1&#39; as unsigned int) as test ;
    +------+
    | test |
    +------+
    |  123 |
    +------+
    1 row in set, 1 warning (0.00 sec)-- 直接按照字符截取,补上了20(不能补19)
    xxxx.test> select cast(&#39;23:12:13&#39; 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(&#39;23:12:13&#39; 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 &#39;timestamp) as test&#39; at line 1-- 这个时间无法转换成datetime
    xxxx.test> select cast(&#39;10:12:32&#39; 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: &#39;10:12:32&#39; |
    +---------+------+--------------------------------------+
    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)
    로그인 후 복사

    为什么不把 where name = 0 中的 0 转换为 &#39;0&#39;

    • 변환 예

      -- 上面遗留的问题,跟系统时间并没有关系。怀疑虽然指定的是datetime,但是内部还是按照timestamp去做的。
      mysql> select now();
      +---------------------+
      | now()               |
      +---------------------+
      | 1999-08-03 14:16:50 |
      +---------------------+
      1 row in set (0.00 sec)
      
      mysql> select cast(&#39;23:12:13&#39; as datetime) as test ;
      +---------------------+
      | test                |
      +---------------------+
      | 2023-12-13 00:00:00 |
      +---------------------+
      1 row in set (0.00 sec)
      로그인 후 복사
      name = 00'0'으로 변환해 보시는 건 어떨까요?

      0을 '0'으로 바꾸는 등 숫자를 문자로 변환하면 쿼리 결과는 필드가 '0'과 같다는 것만 나올 수 있지만 실제로는 ' a0', '00' , 이는 실제로 사용자가 원하는 0입니다. 결국 사용자가 숫자 0을 지정했기 때문에 MySQL은 여전히 ​​사용자의 요청을 기준으로 삼습니다. 그렇지 않으면 '00'이 사용자에게 반환되지 않습니다. .
      • 요약
      • 위 내용으로 시작 질문을 설명할 수 있습니다.

      위 그림의 예시를 인증 우회에 사용할 수 있나요?


      🎜보충제🎜rrreee🎜🎜🎜

      위 내용은 MySQL의 암시적 변환 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    관련 라벨:
    원천:php.cn
    본 웹사이트의 성명
    본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
    인기 튜토리얼
    더>
    최신 다운로드
    더>
    웹 효과
    웹사이트 소스 코드
    웹사이트 자료
    프론트엔드 템플릿