MySQL 8.0 欄位資料型別不對導致查詢錯誤
阿新 • • 發佈:2018-12-20
背景: 在生產環境命名匯入了相同條數的記錄,但是開發人員查詢出來的資料行數不一樣。 分析: 後來經過比對和執行計劃分析是因為where條件後的表示時間欄位型別不一致導致的。 模擬分析: mysql> create table ta(id int not null auto_increment primary key,createtime datetime); mysql> create table tb(id int not null auto_increment primary key,createtime varchar(30)); 向表中插入相同條數的記錄: insert into ta(createtime) select now() union all select date_add(now(),interval 7 day); insert into tb(createtime) select now() union all select date_add(now(),interval 7 day); --兩個表查詢: mysql> select * from ta; +----+---------------------+ | id | createtime | +----+---------------------+ | 1 | 2018-11-07 16:45:32 | | 2 | 2018-11-14 16:45:32 | +----+---------------------+ 2 rows in set (0.00 sec) mysql> select * from tb; +----+---------------------+ | id | createtime | +----+---------------------+ | 1 | 2018-11-07 16:46:28 | | 2 | 2018-11-14 16:46:28 | +----+---------------------+ 2 rows in set (0.00 sec) --過濾條件查詢: mysql> select * from ta where createtime >'2018-11-1'; +----+---------------------+ | id | createtime | +----+---------------------+ | 1 | 2018-11-07 16:45:32 | | 2 | 2018-11-14 16:45:32 | +----+---------------------+ 2 rows in set (0.00 sec) mysql> select * from tb where createtime >'2018-11-1'; +----+---------------------+ | id | createtime | +----+---------------------+ | 2 | 2018-11-14 16:46:28 | +----+---------------------+ 1 row in set (0.00 sec) 可以看到因為tb表的createtime欄位定義為varchar只過濾了一條記錄。 --正確的寫法: mysql> select * from tb where createtime >'2018-11-01'; +----+---------------------+ | id | createtime | +----+---------------------+ | 1 | 2018-11-07 16:46:28 | | 2 | 2018-11-14 16:46:28 | +----+---------------------+ 2 rows in set (0.00 sec) mysql> select * from ta where createtime >'2018-11-01'; +----+---------------------+ | id | createtime | +----+---------------------+ | 1 | 2018-11-07 16:45:32 | | 2 | 2018-11-14 16:45:32 | +----+---------------------+ 2 rows in set (0.00 sec) 實際上因為資料型別不一致,ta表的查詢發生了隱式資料型別轉換。 建立索引檢視執行計劃: mysql> create index ix_createtime on ta(createtime); Query OK, 0 rows affected (0.64 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> create index ix_createtime on tb(createtime); Query OK, 0 rows affected (0.12 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select * from ta where createtime >'2018-11-1'; +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+--------------------------+ | 1 | SIMPLE | ta | NULL | index | ix_createtime | ix_createtime | 6 | NULL | 2 | 100.00 | Using where; Using index | +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+--------------------------+ 1 row in set, 1 warning (0.01 sec) mysql> explain select * from tb where createtime >'2018-11-1'; +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+--------------------------+ | 1 | SIMPLE | tb | NULL | range | ix_createtime | ix_createtime | 123 | NULL | 1 | 100.00 | Using where; Using index | +----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+--------------------------+ 1 row in set, 1 warning (0.00 sec) 結論:當createtime這類明確看字義應該定義datetime型別的結果定義為varchar型別,此時直接寫 >'2018-11-1' 表示的大於2018-11-10 日零點的資料,準確的寫法應是用函式str_to_date進行轉換。