日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

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

發(fā)布時(shí)間:2025/6/15 数据库 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于MySQL count(distinct) 逻辑的另一个bug_ 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?

http://dinglin.iteye.com/blog/1982176

?


背景

???????? 上一篇博文(鏈接)介紹了count distinct的一個(gè)bug。解決完以后發(fā)現(xiàn)客戶的SQL語句仍然返回錯(cuò)誤結(jié)果(0), 再查原因,發(fā)現(xiàn)了另外一個(gè)bug。也就是說,這個(gè)SQL語句觸發(fā)了兩個(gè)bug -_-

?

這里只說第二個(gè),將問題簡(jiǎn)化后復(fù)現(xiàn)如下,影響已知的所有版本 。

?

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值設(shè)為各不相同。

?

與上個(gè)bug類似,5.5+的版本直接復(fù)現(xiàn);5.1版本需要修改的是max_heap_table_size參數(shù),而由于max_heap_table_size的最小值限制不能設(shè)置為1024,需要的測(cè)試數(shù)據(jù)量大些,但原理類似。

?

原因分析

???????? Count(distinct case when xxx then f end)的語義就是計(jì)算字段f的去重總數(shù),計(jì)算流程細(xì)節(jié)參看前一篇。這里直接給出tmp_table_size不夠大時(shí)的流程,便于說明此問題。

??

???????? ? 流程:

1、? 構(gòu)造一個(gè)unique 集合A1, 將滿足條件的結(jié)果插入A1中(計(jì)算了case when之后的值)

2、? 插入item過程中若大小超過tmp_table_size,則將A1暫時(shí)寫到文件中,再構(gòu)造集合A2

3、? 重復(fù)步驟2直到所有的item插入完成

因此若item很多則可能重復(fù)生成多個(gè)集合A1~An。

4、? 對(duì)A1~An作合并操作。由于只是每個(gè)集合A保證unique,因此需要做類似歸并排序的操作(實(shí)際上不需要排序,只是掃一遍)

5、? 合并加和操作本來只需要去重和去掉NULL值即可,但為了復(fù)用代碼,對(duì)于每個(gè)item,重新計(jì)算了一次結(jié)果的合法性,也就是,再判斷一次case when是否正確。

6、? 不幸的是,計(jì)算結(jié)果合法性的這些case when,其實(shí)是共同的一個(gè):最后一行。

?

因此最后的結(jié)果是正確值還是0,就取決于最后一行的case when的結(jié)果。

案例分析

以上面這個(gè)case為例。由于使用主鍵,最后的一行必然是id=64的那一行。這樣在合并的時(shí)候,若條件是id<=64 這些值都被認(rèn)為符合條件可以合并。而最后一個(gè)語句的情況,最后一行id<=64不成立

?

作為驗(yàn)證可以看一下這個(gè)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,正確

?? 可以看到,其實(shí)tb2和tb1的數(shù)據(jù)內(nèi)容是一樣的,只是tb2沒有索引且數(shù)據(jù)倒置插入,因此查詢的最后一行的id是1,滿足id<=63, 結(jié)果記入就正確了。

?

解決方法

???????? 調(diào)高tmp_table_size也是一種直接的方法,但是不治本,因?yàn)橹灰獫M足條件的行數(shù)足夠多,就會(huì)出現(xiàn)這個(gè)問題。

?

???????? 當(dāng)然本質(zhì)上這是一個(gè)bug。

???????? 代碼上,對(duì)于已經(jīng)走到合并操作的這個(gè)邏輯,其實(shí)前面在構(gòu)造各個(gè)集合A1~An的時(shí)候,已經(jīng)驗(yàn)證過條件合法,其實(shí)在合并的時(shí)候,可以直接做去重操作即可。

轉(zhuǎn)載于:https://www.cnblogs.com/kaka100/p/3447173.html

總結(jié)

以上是生活随笔為你收集整理的关于MySQL count(distinct) 逻辑的另一个bug_的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。