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)mysql

查看建表語句,發現這個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  
sql

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中的表現是一致的,對於這種隱式轉換仍是要多加註意。ide




來自 「 ITPUB博客 」 ,連接:http://blog.itpub.net/26736162/viewspace-2145096/,如需轉載,請註明出處,不然將追究法律責任。oop

相關文章
相關標籤/搜索