以前也總給業務優化SQL,隱式轉換也很是常見,可是下面這個圖,一會兒還真解釋不清楚。mysql
下面的內容,最終就是爲了解釋下面這個圖。sql
-- 注意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=latin1
1 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
自動改爲 '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 |
+---------------------+
以前也總給業務優化SQL,隱式轉換也很是常見,可是下面這個圖,一會兒還真解釋不清楚。函數
下面的內容,最終就是爲了解釋下面這個圖。優化
-- 注意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=latin1
1 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
自動改爲 '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 |
+---------------------+