关于MySQL count(distinct) 逻辑的另一个bug_
?
http://dinglin.iteye.com/blog/1982176
?
背景
???????? 上一篇博文(鏈接)介紹了count distinct的一個bug。解決完以后發現客戶的SQL語句仍然返回錯誤結果(0), 再查原因,發現了另外一個bug。也就是說,這個SQL語句觸發了兩個bug -_-
?
這里只說第二個,將問題簡化后復現如下,影響已知的所有版本 。
?
| drop table if exists tb; set tmp_table_size=1024; create table tb(id int auto_increment primary key, v varchar(32))engine=myisam charset=gbk; insert into tb(v) values("aaa"); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); insert into tb(v) (select v from tb); ? update tb set v=concat(v, id); select count(distinct case when id<=64 then id end) from tb; ? 返回64,正確 select count(distinct case when id<=63 then id end) from tb; ?? 返回0 |
上述中update語句的目的是將所有的v值設為各不相同。
?
與上個bug類似,5.5+的版本直接復現;5.1版本需要修改的是max_heap_table_size參數,而由于max_heap_table_size的最小值限制不能設置為1024,需要的測試數據量大些,但原理類似。
?
原因分析
???????? Count(distinct case when xxx then f end)的語義就是計算字段f的去重總數,計算流程細節參看前一篇。這里直接給出tmp_table_size不夠大時的流程,便于說明此問題。
??
???????? ? 流程:
1、? 構造一個unique 集合A1, 將滿足條件的結果插入A1中(計算了case when之后的值)
2、? 插入item過程中若大小超過tmp_table_size,則將A1暫時寫到文件中,再構造集合A2
3、? 重復步驟2直到所有的item插入完成
因此若item很多則可能重復生成多個集合A1~An。
4、? 對A1~An作合并操作。由于只是每個集合A保證unique,因此需要做類似歸并排序的操作(實際上不需要排序,只是掃一遍)
5、? 合并加和操作本來只需要去重和去掉NULL值即可,但為了復用代碼,對于每個item,重新計算了一次結果的合法性,也就是,再判斷一次case when是否正確。
6、? 不幸的是,計算結果合法性的這些case when,其實是共同的一個:最后一行。
?
因此最后的結果是正確值還是0,就取決于最后一行的case when的結果。
案例分析
以上面這個case為例。由于使用主鍵,最后的一行必然是id=64的那一行。這樣在合并的時候,若條件是id<=64 這些值都被認為符合條件可以合并。而最后一個語句的情況,最后一行id<=64不成立
?
作為驗證可以看一下這個case
| CREATE TABLE `tb2` (?? `id` int(11) NOT NULL ,?? `v` varchar(32) DEFAULT NULL ) ENGINE=MyISAM? DEFAULT CHARSET=gbk; insert into tb2 (select * from tb order by id desc); select count(distinct case when id<=63 then id end) from tb2; 返回63,正確 |
?? 可以看到,其實tb2和tb1的數據內容是一樣的,只是tb2沒有索引且數據倒置插入,因此查詢的最后一行的id是1,滿足id<=63, 結果記入就正確了。
?
解決方法
???????? 調高tmp_table_size也是一種直接的方法,但是不治本,因為只要滿足條件的行數足夠多,就會出現這個問題。
?
???????? 當然本質上這是一個bug。
???????? 代碼上,對于已經走到合并操作的這個邏輯,其實前面在構造各個集合A1~An的時候,已經驗證過條件合法,其實在合并的時候,可以直接做去重操作即可。
轉載于:https://www.cnblogs.com/kaka100/p/3447173.html
總結
以上是生活随笔為你收集整理的关于MySQL count(distinct) 逻辑的另一个bug_的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hibernate配置文件,省的到处找了
- 下一篇: 30张图 讲述真实的人性