1. 程式人生 > >燒腦資料庫演算法題 Postgresql 找出陣列中重複的手機號

燒腦資料庫演算法題 Postgresql 找出陣列中重複的手機號

備註:此演算法也適用於人脈搜尋(比如共同的好友等)

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秒.