日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

关于MySQL count(distinct) 逻辑的另一个bug_

發布時間:2025/6/15 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于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_的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。