# 背景
現(xiàn)實場景中經(jīng)常會遇到字符串字段去重計數(shù)的場景,常見方式如下:
- 最簡單的是count(distinct),性能最差,不推薦
- group by 后count(1),充分利用分布式并行計算的優(yōu)點,但是容易遇到數(shù)據(jù)傾斜,總體上是推薦
除了以上兩種方式,還可以借助rbm進行去重計數(shù),接下來介紹代理鍵+rbm的方法
# 方案介紹
rbm是bitset的優(yōu)化,把long型變量分為高32位和低32位分別存放,高32位用作索引(具體原理可參考https://blog.csdn.net/u011624157/article/details/108432494)。
rbm是很好用,但是只能存放int、long等數(shù)字類型,要對字符串做去重的話需要把字符串映射成數(shù)值,方案有兩種:
- 取hashcode,實現(xiàn)較簡單,但數(shù)據(jù)量太大會有hash碰撞,僅適用于中小規(guī)模數(shù)據(jù)量
- 使用row_number函數(shù)生成數(shù)字類型代理鍵,實現(xiàn)略復(fù)雜,需要做一個中間表,但是去重精確,適用于大數(shù)據(jù)量場景
## 詳細實現(xiàn)
接下來說代理鍵+rbm的詳細代碼
-? 1、先做代理鍵
```sql
select
? ? log_id,
? ? id
from(
select
? ? log_id,
? ? row_sequence() as id --使用row_sequence函數(shù)生成代理鍵
from(
select
? ? log_id
from
(
? ? select log_id
? ? from base_log
) t1
group by log_id
) a) a
distribute by id
;
```
- 2、根據(jù)rbm udaf進行去重
# 附:代理鍵名詞解釋
## 代理鍵
在關(guān)系型數(shù)據(jù)庫設(shè)計中,代理鍵(英語:surrogate key)是在當(dāng)資料表中的候選鍵都不適合當(dāng)主鍵時,例如資料太長,或是意義層面太多,就會請一個無意義的但唯一的字段來代為作主鍵。
代理鍵是:
Surrogate (1) – Hall, Owlett and Codd (1976)
一個代理鍵值確定了外部世界的一個實體。代理鍵值是數(shù)據(jù)庫生成的,從來不顯示給用戶或應(yīng)用程序看。
Surrogate (2) – Wieringa and De Jonge (1991)
一個代理鍵值確定了數(shù)據(jù)庫中的一個對象。代理鍵值是數(shù)據(jù)庫生成的,用戶或應(yīng)用程序看不到它。
在實踐中,代理鍵值通常是個自動遞增的數(shù)字。在Sybase或SQL Server,用identity column標(biāo)識代理鍵,PostgreSQL用serial,Oracle用SEQUENCE,在MySQL用標(biāo)記有AUTO_INCREMENT的字段。
## 何時使用代理鍵
以中華人民共和國的十八位身份證號為例,從左往右為六位數(shù)字地址碼,八位數(shù)字出生日期碼,三位數(shù)字順序碼和一位數(shù)字校驗碼。
一家公司想要將它的客戶記入數(shù)據(jù)庫,以客戶的身份證號作為主鍵當(dāng)然是可以的;但是這18位身份證號是用于標(biāo)識大陸13多億人口的,一家公司的客戶顯然沒有這么多,所以用18位的數(shù)字作為主鍵有點浪費空間。
另外,身份證號中包含了地區(qū)、生日信息,若以身份證號為主鍵,要不要另開字段記錄客戶的地區(qū)、生日也是個問題。如果不另開字段,從主鍵(身份證號)中提取地區(qū)、生日有點麻煩;如果開字段,主鍵和地區(qū)、生日字段的數(shù)據(jù)存在冗余。
所以,一般的做法是,根本不記錄客戶的身份證號(除非有其他需求),用一個代理鍵作為主鍵,另開字段記錄客戶的地區(qū)、生日等信息。