MySQL和Oracle中的隱式轉換
今天在處理一個問題的時候,需要根據其他部門提供的sql語句對一個表中的資料進行了篩查。
語句類似下面的形式
> SELECT MAX_LEVEL,LOGOUT_TIME,CURRENT_DATE AS NOWTIME,cn_master FROM t_test_october_back_a WHERE ID in ( 100, 200, 300, 400, 500) ;
+-----------+---------------+------------+-----------+
| MAX_LEVEL | LOGOUT_TIME | NOWTIME | ID|
+-----------+---------------+------------+-----------+
| 1 | 1440407918000 | 2015-08-31 | 100|
| 100| 1441009281000 | 2015-08-31 | 200|
| 1 | 1440408002000 | 2015-08-31 | 300|
+-----------+---------------+------------+-----------+
x rows in set, 65535 warnings (10.98 sec)
本來這一個簡單查詢就完成了,也得到了業務部門需要的資料情況,但是檢視最後一行的內容,還是有些蹊蹺。如果觀察仔細,對於這種id的資料查詢,走索引的話,絕對不會再10秒左右,這是第一個奇怪的地方,第二個奇怪的地方就是65535 warnings,一個簡單查詢怎麼會有這麼多的warnings
最開始怕show warnings的時候會一下子顯示出來6萬多行資料,還小小擔心了一下,結果輸出的結果只有64行。
>show warnings;
+---------+------+----------------------------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '3506996@abc.com' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '28366@abc.com' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '81700700@abc.com' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '391112900@abc.com' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '867964771@abc.com' |
...
64 rows in set (0.00 sec)
語句類似下面的形式
> SELECT MAX_LEVEL,LOGOUT_TIME,CURRENT_DATE AS NOWTIME,cn_master FROM t_test_october_back_a WHERE ID in ( 100, 200, 300, 400, 500) ;
+-----------+---------------+------------+-----------+
| MAX_LEVEL | LOGOUT_TIME | NOWTIME | ID|
+-----------+---------------+------------+-----------+
| 1 | 1440407918000 | 2015-08-31 | 100|
| 100| 1441009281000 | 2015-08-31 | 200|
| 1 | 1440408002000 | 2015-08-31 | 300|
+-----------+---------------+------------+-----------+
x rows in set, 65535 warnings (10.98 sec)
本來這一個簡單查詢就完成了,也得到了業務部門需要的資料情況,但是檢視最後一行的內容,還是有些蹊蹺。如果觀察仔細,對於這種id的資料查詢,走索引的話,絕對不會再10秒左右,這是第一個奇怪的地方,第二個奇怪的地方就是65535 warnings,一個簡單查詢怎麼會有這麼多的warnings
最開始怕show warnings的時候會一下子顯示出來6萬多行資料,還小小擔心了一下,結果輸出的結果只有64行。
>show warnings;
+---------+------+----------------------------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '3506996@abc.com' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '28366@abc.com' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '81700700@abc.com' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '391112900@abc.com' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '867964771@abc.com' |
...
64 rows in set (0.00 sec)
檢視建表語句,發現這個id列是varchar型別的。
>show create table t_test_october_back_a;
| t_tl_october_back_a | CREATE TABLE `t_tl_october_back_a` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`id` varchar(60) NOT NULL,
`area_server` varchar(80) NOT NULL,
`max_level` tinyint(4) NOT NULL,
`logout_time` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_october_back_cn_master` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2042252493 DEFAULT CHARSET=gbk |
我們就順著錯誤資訊來看看,把警告中的部分直接作為引數,發現查詢的時間極快。
>select *from t_tl_october_back_a where cn_master ='867964771@abc.com';
+-------+-------------------------+---------------------------+-----------+---------------+
| id | cn_master | area_server | max_level | logout_time |
+-------+-------------------------+---------------------------+-----------+---------------+
| 18723 | 867964771@abc.com | abcd-abcd | 104 | 1434446979000 |
+-------+-------------------------+---------------------------+-----------+---------------+
1 row in set (0.00 sec)
對此我們來透過執行計劃來簡單對比一下。
如果使用數字的方式,會走全表掃描
> explain SELECT MAX_LEVEL,LOGOUT_TIME FROM t_tl_october_back_a WHERE CN_MASTER in ( 100, 200);
+----+-------------+---------------------+------+-------------------------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+------+-------------------------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | t_test_october_back_a | ALL | idx_test_october_back_cn_master | NULL | NULL | NULL | 28597841 | Using where |
+----+-------------+---------------------+------+-------------------------------+------+---------+------+----------+-------------+
1 row in set (0.00 sec)
而如果使用字元的形式,執行計劃就會走索引,執行效果就是預期的樣子。
>explain SELECT MAX_LEVEL,LOGOUT_TIME FROM t_tl_october_back_a WHERE CN_MASTER = '100' ;
+----+-------------+---------------------+-------+-------------------------------+-------------------------------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------+-------------------------------+-------------------------------+---------+-------+------+-------+
| 1 | SIMPLE | t_test_october_back_a | const | idx_test_october_back_cn_master | idx_test_october_back_cn_master | 122 | const | 1 | |
+----+-------------+---------------------+-------+-------------------------------+-------------------------------+---------+-------+------+-------+
1 row in set (0.00 sec)
透過上面的例子可以看到,在查詢的時候丟擲的警告,其實就是在做型別轉換的時候本來輸出的是數字型別,就會嘗試做隱式轉換,而那個65535只是一個最大限制而已,表中的資料其實已經遠遠超過千萬。
這個時候我們大體感受到了隱式轉換在MySQL中的一些影響,我們來看看在MySQL 5.6和Oracle中的表現如何。
###############
MYSQL 5.6
mysql> select version();
+-------------------------------------------+
| version() |
+-------------------------------------------+
| 5.6.23-enterprise-commercial-advanced-log |
+-------------------------------------------+
mysql> create table test (id1 int,id2 varchar(10));
mysql> insert into test values(1,'1');
mysql> insert into test values(2,'2');
mysql> insert into test values(3,'3');
mysql> commit;
mysql> create index idx_id1 on test(id1);
mysql> create index idx_id2 on test(id2);
隱式轉換,有數字轉換為字元的時候,直接走了索引掃描
mysql> explain select * from test where id1='1';
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | test | ref | idx_id1 | idx_id1 | 5 | const | 1 | NULL |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------+
1 row in set (0.01 sec)
隱式轉換,由字元轉換為數字的時候,直接走了全表掃描
mysql> explain select *from test where id2=2;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | test | ALL | idx_id2 | NULL | NULL | NULL | 3 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
###############
Oracle 11gR2
sqlplus -v
SQL*Plus: Release 10.2.0.3.0 - Production
>show create table t_test_october_back_a;
| t_tl_october_back_a | CREATE TABLE `t_tl_october_back_a` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`id` varchar(60) NOT NULL,
`area_server` varchar(80) NOT NULL,
`max_level` tinyint(4) NOT NULL,
`logout_time` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_test_october_back_cn_master` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2042252493 DEFAULT CHARSET=gbk |
我們就順著錯誤資訊來看看,把警告中的部分直接作為引數,發現查詢的時間極快。
>select *from t_tl_october_back_a where cn_master ='867964771@abc.com';
+-------+-------------------------+---------------------------+-----------+---------------+
| id | cn_master | area_server | max_level | logout_time |
+-------+-------------------------+---------------------------+-----------+---------------+
| 18723 | 867964771@abc.com | abcd-abcd | 104 | 1434446979000 |
+-------+-------------------------+---------------------------+-----------+---------------+
1 row in set (0.00 sec)
對此我們來透過執行計劃來簡單對比一下。
如果使用數字的方式,會走全表掃描
> explain SELECT MAX_LEVEL,LOGOUT_TIME FROM t_tl_october_back_a WHERE CN_MASTER in ( 100, 200);
+----+-------------+---------------------+------+-------------------------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+------+-------------------------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | t_test_october_back_a | ALL | idx_test_october_back_cn_master | NULL | NULL | NULL | 28597841 | Using where |
+----+-------------+---------------------+------+-------------------------------+------+---------+------+----------+-------------+
1 row in set (0.00 sec)
而如果使用字元的形式,執行計劃就會走索引,執行效果就是預期的樣子。
>explain SELECT MAX_LEVEL,LOGOUT_TIME FROM t_tl_october_back_a WHERE CN_MASTER = '100' ;
+----+-------------+---------------------+-------+-------------------------------+-------------------------------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------+-------------------------------+-------------------------------+---------+-------+------+-------+
| 1 | SIMPLE | t_test_october_back_a | const | idx_test_october_back_cn_master | idx_test_october_back_cn_master | 122 | const | 1 | |
+----+-------------+---------------------+-------+-------------------------------+-------------------------------+---------+-------+------+-------+
1 row in set (0.00 sec)
透過上面的例子可以看到,在查詢的時候丟擲的警告,其實就是在做型別轉換的時候本來輸出的是數字型別,就會嘗試做隱式轉換,而那個65535只是一個最大限制而已,表中的資料其實已經遠遠超過千萬。
這個時候我們大體感受到了隱式轉換在MySQL中的一些影響,我們來看看在MySQL 5.6和Oracle中的表現如何。
###############
MYSQL 5.6
mysql> select version();
+-------------------------------------------+
| version() |
+-------------------------------------------+
| 5.6.23-enterprise-commercial-advanced-log |
+-------------------------------------------+
mysql> create table test (id1 int,id2 varchar(10));
mysql> insert into test values(1,'1');
mysql> insert into test values(2,'2');
mysql> insert into test values(3,'3');
mysql> commit;
mysql> create index idx_id1 on test(id1);
mysql> create index idx_id2 on test(id2);
隱式轉換,有數字轉換為字元的時候,直接走了索引掃描
mysql> explain select * from test where id1='1';
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | test | ref | idx_id1 | idx_id1 | 5 | const | 1 | NULL |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------+
1 row in set (0.01 sec)
隱式轉換,由字元轉換為數字的時候,直接走了全表掃描
mysql> explain select *from test where id2=2;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | test | ALL | idx_id2 | NULL | NULL | NULL | 3 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
###############
Oracle 11gR2
sqlplus -v
SQL*Plus: Release 10.2.0.3.0 - Production
SQL> create table test (id1 number,id2 varchar2(10));
SQL> insert into test values(1,'1');
SQL> begin
2 for i in 1..100 loop
3 insert into test values(i,chr(39)||i||chr(39));
4 end loop;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats('TEST','TEST',CASCADE=>TRUE);
隱式轉換,由數字轉換為字元的時候,直接走了索引掃描
SQL> explain plan for select *from test where id1='2';
SQL> select *from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3847161316
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST | 1 | 7 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_ID1 | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
PLAN_TABLE_OUTPUT
2 - access("ID1"=2)
隱式轉換,由字元轉換為數字的時候,直接走了全表掃描
SQL> explain plan for select *from test where id2=3;
SQL> select *from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1357081020
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TEST | 1 | 7 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 - filter(TO_NUMBER("ID2")=3)
可見在這個方面MySQL和Oracle中的表現是一致的,對於這種隱式轉換還是要多加註意。
SQL> insert into test values(1,'1');
SQL> begin
2 for i in 1..100 loop
3 insert into test values(i,chr(39)||i||chr(39));
4 end loop;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats('TEST','TEST',CASCADE=>TRUE);
隱式轉換,由數字轉換為字元的時候,直接走了索引掃描
SQL> explain plan for select *from test where id1='2';
SQL> select *from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3847161316
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST | 1 | 7 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_ID1 | 1 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
PLAN_TABLE_OUTPUT
2 - access("ID1"=2)
隱式轉換,由字元轉換為數字的時候,直接走了全表掃描
SQL> explain plan for select *from test where id2=3;
SQL> select *from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1357081020
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TEST | 1 | 7 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 - filter(TO_NUMBER("ID2")=3)
可見在這個方面MySQL和Oracle中的表現是一致的,對於這種隱式轉換還是要多加註意。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26736162/viewspace-2145096/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 從Java的型別轉換看MySQL和Oracle中的隱式轉換(二)Java型別MySqlOracle
- Oracle 隱式轉換Oracle
- js顯式轉換和隱式轉換JS
- mysql隱式轉換問題MySql
- MySQL 隱式型別轉換MySql型別
- ORACLE中的隱式資料型別轉換(一)Oracle資料型別
- Scala - 隱式轉換和隱式引數
- scala中隱式轉換之隱式轉換呼叫類中本不存在的方法
- MySQL索引失效之隱式轉換MySql索引
- 淺談Oracle中隱式型別轉換規律和影響Oracle型別
- Spark中的三種隱式轉換Spark
- Java資料型別的顯式轉換和隱式轉換Java資料型別
- 談談 MySQL 隱式型別轉換MySql型別
- oracle和mysql的行列轉換OracleMySql
- oracle資料隱式轉換規則Oracle
- javascript 隱式轉換JavaScript
- sql隱式轉換SQL
- java隱式轉換Java
- Scala隱式轉換與隱式引數
- 【隱式轉換】注意隱式轉換將導致索引無法使用索引
- Oracle隱式型別轉換導致索引失效Oracle型別索引
- Scala Essentials: 隱式轉換
- mysql 字串和數字比,字串會隱式轉換為數字0MySql字串
- [] == ![],走進==隱式轉換的世界
- mysql和oracle字串編碼轉換函式,字串轉位元組函式例子MySqlOracle字串編碼函式
- 評“MySQL 隱式轉換引起的執行結果錯誤”MySql
- Solidity語言學習筆記————11、隱式轉換和顯式轉換Solid筆記
- 雜記四:scala 柯理化和隱式轉換
- oracle資料型別隱式轉換----- 應急方案Oracle資料型別
- oracle執行計劃------未走索引,隱式轉換的坑Oracle索引
- JavaScript隱式型別轉換JavaScript型別
- 【C++】禁止隱式轉換C++
- MySQL隱式轉化整理MySql
- 你所忽略的js隱式轉換JS
- C++隱式型別的轉換C++型別
- 資料型別的隱式轉換資料型別
- 有趣的JavaScript隱式型別轉換JavaScript型別
- 瞭解MySQL的隱式轉化MySql