燒腦資料庫演算法題 Postgresql 找出陣列中重複的手機號
阿新 • • 發佈:2019-01-22
備註:此演算法也適用於人脈搜尋(比如共同的好友等)
so 思想很重要,工具不重要,思想錯了幹得再多也是白乾.有了思想你也可以無限擴充套件
搜尋人脈關係比查重複的手機號快的多,重複的手機號畢竟要全表比較,人脈搜尋關係只需要掃描相關的資料即可,估計應該可以在100毫秒這個級別,我沒有樣本資料所以沒法測量.
示例資料採用Postgresql,有興趣的朋友可以試試,博主測試環境500W去重大約用時53秒.
使用到的陣列函式點PostgreSQL 陣列函式擴充套件
現實生活中一個人可能會有多個手機號,因此設計系統時必須考慮使用者和手機號的唯一性.
因為實際應用比本案例複雜,主從表實踐下來的效果不太滿意,因此改用陣列儲存電話號碼,以下為指令碼.
1.建立表
/****************************************************************************************
使用者手機資訊表
drop table if exists umobiles;
delete from umobiles
****************************************************************************************/
create table umobiles(
objectid bigint not null, --使用者唯一編號
mobiles text[] not null, --使用者電話號碼
constraint pk_umobiles_objectid primary key(objectid) with (fillfactor=80)
) with (fillfactor=80);
create index idx_umobiles_mobiles on umobiles using gin(mobiles);
2.建立相關函式
/****************************************************************************************
建立隨機生成手機號函式
drop function if exists mobiles_rand();
****************************************************************************************/
create or replace function mobiles_rand()
returns text[]
as $$
select array_agg('1'|| ((random()*(999999999-100000000)+0)::integer)) from generate_series(1,(random()*(3-1)+1)::bigint);
$$ language sql strict;
/****************************************************************************************
新增資料至使用者手機資訊表函式,每執行一次插入1000行
drop function if exists umobiles_add();
****************************************************************************************/
create or replace function umobiles_add(bigint)
returns bigint
as $$
with cte as(
insert into umobiles(objectid,mobiles)
select id as objectid,rand_mobiles() as mobiles from generate_series($1,$1 + 999) as id
returning objectid
)select max(objectid) from cte;
$$ language sql strict;
3.匯入測試資料
/****************************************************************************************
遞迴插入資料,再燒腦,直接燒死你們,哈哈
每次插入1000行,直到插入5000000資料
****************************************************************************************/
with recursive inserts(id) as (
values(1::bigint)
union all
select umobiles_add(id)+1 from inserts as p
where id<=5000000
)select * from inserts;
--Time: 203754.293 ms (03:23.754)
select count(*) from umobiles;
3.找出重複的手機號
因為是隨機的,所以每個人的結果都是不同的.
/****************************************************************************************
計算是否有重複的手機號,按資料分片原則一次檢查$2行資料
$1:使用者編號
$2:每次檢查的行數
返回null表示手機號沒有重複,否則返回記錄唯一編號
drop function if exists umobiles_repeat(bigint,integer);
****************************************************************************************/
create or replace function umobiles_repeat(iuserid bigint,ilimit integer)
returns table(objectids bigint[])
as $$
with cte as(
select objectid from umobiles where objectid>=$1 order by objectid limit $2
),mobiles as(
select objectid,unnest(mobiles) as mobile from umobiles where objectid = any(select objectid from cte)
),counts as(
select array_agg(objectid) as objectids,count(*) as c from mobiles as ta group by mobile
)select array_repeat(objectids) from counts where c>1
$$ language sql;
--按資料分片原則一次檢查一萬行,直到所有資料處理完成.
select umobiles_repeat(1,10000)
批量一次性處理自己動動腦,多動腦有利於預防老年痴呆,博主測試環境一次性完成500W去重大約用時53秒.