隐式转化规则
官方文档中关于隐式转化的规则是如下描述的:
인수 중 하나 이상이 NULL이면 비교 결과는 NULL입니다. 단, NULL 안전 < ; => 동등 비교 연산자. NULL <=> NULL, 결과는 true입니다. 변환이 필요하지 않습니다.
비교 연산의 두 인수가 모두 문자열이면 문자열로 비교됩니다.
두 인수가 모두 정수이면 정수로 비교됩니다.
16진수 값은 다음과 같이 처리됩니다. 숫자와 비교되지 않은 경우 이진 문자열입니다.
인수 중 하나가 TIMESTAMP 또는 DATETIME 열이고 다른 인수가 상수인 경우 비교가 수행되기 전에 상수가 타임스탬프로 변환됩니다. 이는 보다 ODBC 친화적이도록 수행됩니다. IN()에 대한 인수에 대해서는 이 작업이 수행되지 않습니다. 안전을 위해 비교를 수행할 때 항상 완전한 날짜/시간, 날짜 또는 시간 문자열을 사용하십시오. 예를 들어 날짜 또는 시간 값과 함께 BETWEEN을 사용할 때 최상의 결과를 얻으려면 CAST()를 사용하여 값을 원하는 데이터 유형으로 명시적으로 변환합니다.
테이블의 단일 행 하위 쿼리는 상수로 간주되지 않습니다. 예를 들어 하위 쿼리가 DATETIME 값과 비교할 정수를 반환하는 경우 비교는 두 개의 정수로 수행됩니다. 정수는 임시 값으로 변환되지 않습니다. 피연산자를 DATETIME 값으로 비교하려면 CAST()를 사용하여 하위 쿼리 값을 DATETIME으로 명시적으로 변환합니다.
인수 중 하나가 10진수 값인 경우 비교는 다른 인수에 따라 달라집니다. 다른 인수가 10진수 또는 정수 값이면 인수는 10진수 값으로 비교되고, 다른 인수가 부동 소수점 값이면 부동 소수점 값으로 비교됩니다.
다른 모든 경우에는 인수가 부동 소수점 값으로 비교됩니다. -포인트(실수) 숫자.
翻译为中文就是:
两个参数至少有一个是 NULL 时,比较的结也是 NULL, 例외是使용 <=> 对两个 NULL 做比较时会返回 1,这两种情况都不需要做类型转换
两个参数duc是字符串,会按 사진자符串来比较,不做类型转换
어디에 条件语句里,字段属性和赋给的条件,当数据类型不一样,这时候是没法直接比较的,需要进行一致转换
对于不符值,如10:12:32等,会变为 0000-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 '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)
조건이 0이나 1로 쓰여지면 모든 이름 필드를 문자에서 정수로 변환한 후 0이나 1과 비교해야 합니다. 모두 문자로 시작하는 문자이므로 모두 0으로 변환되어 반환되는 결과는 모두 행이다.
그런데 누군가가 조건의 0
을 '0'
으로 자동으로 변경하면 어떨까요? 아래를 참조하세요. 0
自动改成 '0'
?见下文。
-- 字符开头,直接是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)
where name = 0
中的 0
转换为 '0'
-- 上面遗留的问题,跟系统时间并没有关系。怀疑虽然指定的是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)
name = 0
의 0
을 '0'
으로 변환해 보시는 건 어떨까요? 0을 '0'으로 바꾸는 등 숫자를 문자로 변환하면 쿼리 결과는 필드가 '0'과 같다는 것만 나올 수 있지만 실제로는 ' a0', '00' , 이는 실제로 사용자가 원하는 0입니다. 결국 사용자가 숫자 0을 지정했기 때문에 MySQL은 여전히 사용자의 요청을 기준으로 삼습니다. 그렇지 않으면 '00'이 사용자에게 반환되지 않습니다. . 위 내용은 MySQL의 암시적 변환 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!