Hologres針對大數據量(億級)、高QPS的UV計算場景,提供近實時預聚合UV計算方案,支持您通過RoaringBitmap,結合周期性調度的方式對數據進行預聚合,實現任意長周期的UV靈活計算。
方案介紹
對于大數據量且對QPS、延遲有一定需求的業務,可以通過Hologres RoaringBitmap,結合周期性調度的方式對數據進行預聚合,實現任意長周期的UV靈活計算。該方案的優缺點及適用場景如下:
優缺點
優點:計算性能好,可以實現高QPS低延遲的UV計算(基數精確去重計算),且可以支持任意周期范圍,滿足業務多種靈活查詢需求。
缺點:需要做一次預計算,并周期性更新聚合表數據,會增加維護任務。
適用場景:大數據量(億級)的任意長周期高QPS的UV計算。
根據業務的實際場景以及數據類型等,RoaringBitmap方案有以下三種實現方法,您可根據業務情況選擇合適的方法。
方法1:INT字段類型的長周期UV計算:適用于對INT類型字段進行UV精確去重計算場景,適用于對單標簽過濾篩選后的基數(即不同值的數量)進行計算。
方法2:TEXT字段類型的長周期UV計算:適用于對TEXT類型字段進行UV精確去重計算場景(需要結合Mapping表使用),適用于對單標簽過濾篩選后的基數進行計算。
方法3(高級方法):分Bucket的長周期UV計算:多用于對畫像類場景中,多個標簽或屬性字段進行交并差計算,以確定人群基數,通過Bucket分桶并行處理查詢,實現人群的高度壓縮,減少對數據的IO操作,提升計算效率。
本文中的UV僅是RoaringBitmap(RB)場景的一種,RB方案也適用于其他基數精確去重的場景,例如直播電商大屏場景,計算商品數、品牌數等。
更多RoaringBitmap函數信息請參見RoaringBitmap函數。
方法1:INT字段類型的RB長周期UV計算
適用場景
適用于大數據量(億級)的任意長周期高QPS的UV計算,且計算UV的字段類型(UID,即用戶ID)是INT類型的場景。
方案流程
該方案的主要流程如下:
步驟一:創建一張用戶明細表,用于存放業務所有維度的明細數據。
步驟二:根據業務邏輯,將明細表按照基礎維度進行GROUP BY聚合,再將聚合出的UID結果轉換為RoaringBitmap形式,存放在聚合結果表中。
步驟三:按照查詢維度查詢聚合結果表,對關鍵的RoaringBitmap字段進行OR運算,去重并統計基數,即可得出對應用戶數UV,統計聚合結果表中的記錄數即可得出PV,達到亞秒級查詢。
步驟一:準備基礎數據
創建RoaringBitmap Extension。
使用RoaringBitmap前需要創建RoaringBitmap Extension,Extension是DB級別的函數,一個DB只需執行一次即可。語法如下:
說明RoaringBitmap Extension只能加載在public Schema下。
CREATE EXTENSION IF NOT EXISTS roaringbitmap;
準備用戶明細表。
準備一張用戶明細表,記錄用戶完整的明細數據。通常來說,明細表會存儲所有明細數據,數據量大,建議使用分區表,按天分區,按天更新,使用更方便。
BEGIN; CREATE TABLE IF NOT EXISTS ods_app_detail ( uid int, country text, prov text, city text, channel text, operator text, brand text, ip text, click_time text, year text, month text, day text, ymd text NOT NULL ) PARTITION BY LIST (ymd) ;--通常來說明細表數據量大,按天分區更方便。 CALL set_table_property('ods_app_detail', 'orientation', 'column'); CALL set_table_property('ods_app_detail', 'bitmap_columns', 'country,prov,city,channel,operator,brand,ip,click_time, year, month, day, ymd'); --distribution_key根據需求設置,根據該表的實時查詢需求,從什么維度做分片能夠取得較好效果即可 CALL set_table_property('ods_app_detail', 'distribution_key', 'uid'); --用于做where過濾條件,包含完整年月日時間字段推薦設為clustering_key和event_time_column CALL set_table_property('ods_app_detail', 'clustering_key', 'ymd'); CALL set_table_property('ods_app_detail', 'event_time_column', 'ymd'); COMMIT;
準備RB聚合結果表。
創建RB聚合結果表,用于存放RoaringBitmap聚合后的結果數據。
聚合后的數據量通常不多(聚合后單天數據量僅幾百萬條),建議聚合結果表使用非分區表,數據回刷時更方便。或者使用按月/季度分區(若按天分區,數據量小,分區多可能會產生較多小文件,影響內存水位)。
以country、prov、city為維度構建基礎維表,并設置為Distribution Key,查詢時可以根據維度查詢。如果存在多個GROUP BY字段(超過3個),建議將使用最頻繁的字段設置為Distribution Key。
將查詢維度字段和日期分區鍵作為主鍵,防止重復插入數據。
將日期過濾字段作為Clustering Key和Event Time Column,加快過濾查詢。
代碼如下:
BEGIN; CREATE TABLE dws_app_rb( rb_uid roaringbitmap, -- UV計算 country text, prov text, city text, ymd text NOT NULL, --日期字段 pv integer, -- PV計算 PRIMARY key(country, prov, city, ymd)--查詢維度和時間作為主鍵,防止重復插入數據 ); CALL set_table_property('dws_app_rb', 'orientation', 'column'); --clustering_key和event_time_column設為日期字段,便于過濾 CALL set_table_property('dws_app_rb', 'clustering_key', 'ymd'); CALL set_table_property('dws_app_rb', 'event_time_column', 'ymd'); --distribution_key設為group by字段 CALL set_table_property('dws_app_rb', 'distribution_key', 'country,prov,city'); END;
步驟二:構建RB聚合結果表
準備好明細表之后,即可進行RB構建,并將結果數據寫入RB聚合表。示例如下:
--示例:查詢半年的數據,構建rb并寫入聚合結果表
INSERT INTO dws_app_rb
SELECT
RB_BUILD_AGG(uid),
country,
prov,
city,
ymd,
COUNT(1)
FROM ods_app_detail
WHERE ymd >= '20231201' AND ymd <='20240502'
GROUP BY country,prov,city,ymd;
當明細表更新后,根據明細表的更新粒度,聚合表的更新可以分為增量更新或者全量更新。二者的區別如下:
聚合表更新方式 | 說明 | 代碼示例 |
增量更新聚合表 | 明細表的數據有規律地更新或新增,例如只更新昨天的分區,即可通過INSERT的方式將增量數據寫入聚合表。 | 只需寫入新增數據:
|
全量更新聚合表 | 明細表的數據無規律地更新,無法快速計算出增量數據,導致無法用新增數據更新聚合表,因此使用全量回刷方式寫入聚合表。 | 使用INSERT OVERWRITE的方式將數據全量回刷至聚合表:
|
步驟三:任意長周期UV查詢
查詢時,從聚合結果表中按照查詢維度進行聚合計算,可以通過聚合表查詢基礎維度任意組合任意時間段的UV、PV,查詢效率相比傳統的SQL更高,通常為毫秒級的查詢響應,也能支持較高的QPS查詢。示例如下:
查詢某天的UV、PV。
SELECT RB_CARDINALITY(RB_OR_AGG(rb_uid)) AS uv, country, prov, city, SUM(pv) AS pv FROM dws_app_rb WHERE ymd = '20240329' GROUP BY country,prov,city;
查詢某個月的UV、PV。
SELECT RB_CARDINALITY(RB_OR_AGG(rb_uid)) AS uv, country, prov, city, SUM(pv) AS pv FROM dws_app_rb WHERE ymd >= '20240301' AND ymd <= '20240331' GROUP BY country,prov,city;
方法2:TEXT字段類型結合Mapping表的RB長周期UV計算
在實際業務場景中,大多數表的ID字段會使用TEXT類型,但是RB不支持TEXT類型,因此需要使用Serial類型構建一張Mapping表,來實現基于RB的UV高效計算。
適用場景
適用于大數據量(億級)的任意長周期高QPS的UV計算,且計算UV的字段是TEXT類型(需要精確去重的字段是TEXT類型)的場景。
方案流程
該方案的主要流程如下:
步驟一:創建一張用戶明細表,用于存放業務所有維度的明細數據。創建一張歷史用戶映射Mapping表,用于存放歷史中每個訪問過的用戶ID(UID)和對應的INT32數值。
步驟二:明細表和用戶映射Mapping表進行JOIN,并按照最細粒度基礎維度進行GROUP BY,將前一天的所有數據根據最大的查詢維度聚合出的UID結果轉換為RoaringBitmap形式,寫入聚合結果表(每天百萬條)。
步驟三:按照查詢維度查詢聚合結果表,對關鍵的RoaringBitmap字段進行OR運算,去重并統計基數,即可得出對應用戶數UV,統計聚合結果表中的記錄數即可得出PV,達到亞秒級查詢。
步驟一:準備基礎數據
創建RoaringBitmap Extension。
使用RoaringBitmap前需要創建RoaringBitmap Extension,Extension是DB級別的函數,一個DB只需執行一次即可。語法如下:
說明RoaringBitmap Extension只能加載在public Schema下。
CREATE EXTENSION IF NOT EXISTS roaringbitmap;
準備用戶明細表。
準備一張用戶明細表,記錄用戶完整的明細數據。通常來說,明細表會存儲所有明細數據,數據量大,建議使用分區表,按天分區,按天更新,使用更方便。
BEGIN; CREATE TABLE IF NOT EXISTS ods_app_detail ( uid text, country text, prov text, city text, channel text, operator text, brand text, ip text, click_time text, year text, month text, day text, ymd text NOT NULL ) PARTITION BY LIST (ymd) ;--通常來說明細表數據量大,按天分區更方便。 CALL set_table_property('ods_app_detail', 'orientation', 'column'); CALL set_table_property('ods_app_detail', 'bitmap_columns', 'country,prov,city,channel,operator,brand,ip,click_time, year, month, day, ymd'); --distribution_key根據需求設置,根據該表的實時查詢需求,從什么維度做分片能夠取得較好效果即可 CALL set_table_property('ods_app_detail', 'distribution_key', 'uid'); --用于做where過濾條件,包含完整年月日時間字段推薦設為clustering_key和event_time_column CALL set_table_property('ods_app_detail', 'clustering_key', 'ymd'); CALL set_table_property('ods_app_detail', 'event_time_column', 'ymd'); COMMIT;
準備RB聚合結果表。
創建RB聚合結果表,用于存放RoaringBitmap聚合后的結果數據。
聚合后的數據量通常不多(聚合后單天數據量僅幾百萬條),建議聚合結果表使用非分區表,數據回刷時更方便?;蛘呤褂冒丛?季度分區(若按天分區,數據量小,分區多可能會產生較多小文件,影響內存水位)。
以country、prov、city為維度構建基礎維表,并設置為Distribution Key,查詢時可以根據維度查詢。如果存在多個GROUP BY字段(超過3個),建議將使用最頻繁的字段設置為Distribution Key。
將查詢維度字段和日期分區鍵作為主鍵,防止重復插入數據。
將日期過濾字段作為Clustering Key和Event Time Column,加快過濾查詢。
代碼如下:
BEGIN; CREATE TABLE dws_app_rb( rb_uid roaringbitmap, -- UV計算 country text, prov text, city text, ymd text NOT NULL, --日期字段 pv integer, -- PV計算 PRIMARY key(country, prov, city, ymd)--查詢維度和時間作為主鍵,防止重復插入數據 ); CALL set_table_property('dws_app_rb', 'orientation', 'column'); --clustering_key和event_time_column設為日期字段,便于過濾 CALL set_table_property('dws_app_rb', 'clustering_key', 'ymd'); CALL set_table_property('dws_app_rb', 'event_time_column', 'ymd'); --distribution_key設為group by字段 CALL set_table_property('dws_app_rb', 'distribution_key', 'country,prov,city'); END;
準備用戶Mapping表。
RoaringBitmap計算的用戶ID字段類型必須為32位INT類型,且越稠密越好,而常見的業務系統或者埋點中的用戶ID很多是字符串類型,因此使用Serial類型(自增的32位INT)構建一張用戶映射表,實現用戶映射的自動管理和穩定映射。
BEGIN; CREATE TABLE uid_mapping ( uid text NOT NULL, uid_int32 serial, PRIMARY KEY (uid) ); --將uid設為clustering_key和distribution_key便于快速查找其對應的int32值 CALL set_table_property('uid_mapping', 'clustering_key', 'uid'); CALL set_table_property('uid_mapping', 'distribution_key', 'uid'); CALL set_table_property('uid_mapping', 'orientation', 'row'); COMMIT;
步驟二:構建RB導入至聚合表
導入與更新用戶映射表。
用戶映射表全量初始化。
首次將全量UID數據導入映射表進行數據初始化。您也可以根據業務的查詢時間范圍,選擇時間按字段過濾,導入部分周期的UID數據至Mapping表。此處以導入半年的UID數據為例。
--根據業務的查詢時間范圍,可以選擇時間字段過濾,導入部分周期的uid數據至mapping表,示例導入半年的數據。 INSERT INTO uid_mapping (uid) B SELECT distinct (uid) FROM ods_app_detail WHERE B.ymd >= '20231201' AND B.ymd <='20240502';
驗證Mapping表的數據正確性。
Mapping表數據導入后,需要驗證其數據是否與明細表的數據一致。
--驗證數據正確性 SELECT COUNT(*) FROM uid_mapping;
同時也需要驗證Serial的連續性。在某些場景中,會使用TRUNCATE+INSERT的方式多次全量初始化Mapping表,而執行TRUNCATE時,Serial類型的Sequence不會被重置,導致Serial值浪費,在多次導入之后會超過Serial的INT32位上限。
--驗證serial的連續性是否正確 SELECT MAX(uid_int32),MIN(uid_int32) FROM uid_mapping;
更新用戶映射表。
隨著明細表的UID數據更新,也需要及時更新用戶映射表的數據。若明細表中僅涉及某天的UID數據更新,可使用INSERT ON CONFLICT更新用戶映射表,如果無法確定UID的更新范圍,可以進行全量數據回刷,但數據量較大可能導致數據回刷時間變長。
示例:使用INSERT ON CONFLICT DO NOTHING更新前一天的用戶映射表,DO NOTHING會保證只更新數據,不會重復寫入。
--更新前一天的用戶映射表數據 INSERT INTO uid_mapping (uid) SELECT distinct (uid) FROM ods_app_detail WHERE ymd = '20240503' ON conflict do nothing;
導入與更新聚合結果表。
更新完用戶映射表后,將數據做聚合運算后插入聚合結果表,主要步驟如下:
通過明細表JOIN用戶映射表,得到聚合條件和對應的
uid_int32
。按照聚合條件做聚合運算后,將數據插入RoaringBitmap聚合結果表,可以根據業務情況選擇聚合周期,如天、月等。
只需進行一次聚合,存放一份數據,數據條數等于UV數量。
插入數據至聚合結果表,命令如下。此處以聚合半年的數據至結果表為例。
WITH aggregation_src AS ( SELECT B.uid_int32, A.country, A.prov, A.city, A.ymd FROM ods_app_detail A INNER JOIN uid_mapping B ON A.uid = B.uid WHERE A.ymd >= '20231201' AND A.ymd <='20240502') INSERT INTO dws_app_rb SELECT RB_BUILD_AGG (uid_int32), country, prov, city, ymd, COUNT(1) FROM aggregation_src GROUP BY country, prov, city, ymd;
更新聚合結果表。
明細表和Mapping表更新后,也需要更新聚合結果表。可以根據實際業務中明細表的更新情況選擇適當的方式更新聚合結果表。
增量更新:明細表周期性寫入和更新,如按天寫入,按天回刷,可以使用INSERT的方式直接寫入最新分區數據。
全量回刷:明細表無固定時間范圍的寫入與更新,即無法識別明細表的增量數據,則無法使用新增數據更新聚合結果表,建議使用INSERT OVERWRITE的方式全量回刷聚合結果表。
步驟三:任意長周期UV查詢
查詢時,從聚合結果表中按照查詢維度做聚合計算,可以通過聚合表查詢基礎維度的任意組合和任意時間段的UV、PV,查詢效率相比傳統的SQL更高,通常為毫秒級的查詢響應,并且能支持較高的QPS查詢。示例如下:
查詢某天的UV、PV。
SELECT country, prov, city, RB_CARDINALITY(RB_OR_AGG(rb_uid)) AS uv, SUM(pv) AS pv FROM dws_app_rb WHERE ymd = '20240329' GROUP BY country,prov,city;
查詢某一個月的UV、PV。
--查一個月的pv、uv SELECT country ,prov ,RB_CARDINALITY(RB_OR_AGG(rb_uid)) AS uv ,SUM(pv) AS pv FROM public.dws_app_rb WHERE ymd >= '20240301' AND ymd <= '20240331' GROUP BY country,prov,city;
方法3(高級方法):分Bucket的RB長周期UV計算
在實際場景中,會存在多個大表關聯計算基數的需求,例如標簽表和屬性表關聯計算人群交并差的基數、行為表和屬性表關聯計算行為基數等的畫像分析場景,通過Hologres RoaringBitmap結合Bucket分桶的方案來實現高效的基數計算。通過分桶,可以將Bitmap拆分成多段打散存儲,充分利用并發計算的能力,實現人群的高度壓縮,減少對數據的IO操作,提升計算效率。
適用場景
適用于大規模數據量(億級以上)的任意長周期UV查詢,通常為精確去重的字段位數長(可能會超過INT32位)或字段基數低(數據的重復度高,例如性別)的場景,更多是多標簽關聯計算人群基數的場景。
原理介紹
Bucket是將字段拆分成不同的桶,讓數據可以打散分布到各個Shard上。如果要實現較好的查詢性能,分桶方式及分桶數量是關鍵因素。結合大多數的實際業務使用情況:
分桶方式:常見的比較高效的分桶方式是通過位運算計算出字段的高位數和低位數,然后將低位存入Bitmap,高位設置為Bucket。
分桶數量:通常實例中的單個Table Group的Shard數不會超過256,對于INT32位的數據來說,int_value>>24(即高8位)存為桶號,低24位存成Bitmap,即可將數據平分到多個Shard上,并且每個Shard上的數據都會聚集好,利于并發。
推薦的分桶計算公式:
方案流程
分Bucket的RB周期UV計算的方案流程如下:
為了降低難度,本示例中的UID字段使用INT類型,如果UID字段是TEXT類型,可參考方法2創建Mapping表來構建RB,分桶的方式不變。
步驟一:準備基礎數據
創建RoaringBitmap Extension。
使用RoaringBitmap前需要創建RoaringBitmap Extension,Extension是DB級別的函數,一個DB只需執行一次即可。語法如下:
說明RoaringBitmap Extension只能加載在public Schema下。
CREATE EXTENSION IF NOT EXISTS roaringbitmap;
準備用戶的行為表和屬性表。
鑒于Bucket的方案通常用在多表關聯計算畫像的場景,因此此處準備兩張表,一張為用戶行為表,記錄完整的明細行為,另一張為屬性表,記錄用戶的屬性,例如性別、年齡等。通過行為和屬性的關聯分析,可以計算任意維度的人群基數。
用戶行為表ods_user_behaviour_detail。
--行為明細數據 BEGIN; CREATE TABLE IF NOT EXISTS ods_user_behaviour_detail ( uid int, operator text, channel text, shop_id text, time text, ymd text NOT NULL ); CALL set_table_property('ods_user_behaviour_detail', 'orientation', 'column'); --distribution_key根據需求設置,根據該表的實時查詢需求,從什么維度做分片能夠取得較好效果即可 CALL set_table_property('ods_user_behaviour_detail', 'distribution_key', 'uid'); --用于做where過濾條件,包含完整年月日時間字段推薦設為clustering_key和event_time_column CALL set_table_property('ods_user_behaviour_detail', 'clustering_key', 'ymd'); CALL set_table_property('ods_user_behaviour_detail', 'event_time_column', 'ymd'); COMMIT;
用戶屬性表dim_userbase。
--用戶屬性數據 BEGIN; CREATE TABLE IF NOT EXISTS dim_userbase ( uid int, age text, gender text, country text, prov text, city text ); CALL set_table_property('dim_userbase', 'orientation', 'column'); CALL set_table_property('dim_userbase', 'distribution_key', 'uid'); COMMIT;
準備RB聚合結果表。
創建RB聚合結果表,用于存放RoaringBitmap聚合后的結果數據。
聚合后的數據量通常不多(聚合后單天數據量僅幾百萬條),建議聚合結果表使用非分區表,數據回刷時更方便?;蛘呤褂冒丛?季度分區(若按天分區,數據量小,分區多可能會產生較多小文件,影響內存水位)。
以country、prov、city為維度構建基礎維表,并設置為Distribution Key,查詢時可以根據維度查詢。如果存在多個GROUP BY字段(超過3個),建議將使用最頻繁的字段設置為Distribution Key。
將查詢維度字段和日期分區鍵作為主鍵,防止重復插入數據。
將日期過濾字段作為Clustering Key和Event Time Column,加快過濾查詢。
行為表聚合結果表dws_user_behaviour_rb。
--行為表聚合結果表 BEGIN; CREATE TABLE dws_user_behaviour_rb( rb_uid roaringbitmap, -- UV計算 bucket int NOT NULL, -- 分桶字段 operator text, channel text, shop_id text, time text, ymd text NOT NULL, PRIMARY key(operator,channel,shop_id,time, ymd,bucket)--查詢維度和時間作為主鍵,防止重復插入數據 ); CALL set_table_property('dws_user_behaviour_rb', 'orientation', 'column'); --clustering key和event_time_column設為日期字段,便于過濾 CALL set_table_property('dws_user_behaviour_rb', 'clustering_key', 'ymd'); CALL set_table_property('dws_user_behaviour_rb', 'event_time_column', 'ymd'); --將分桶字段bucket設置成distribution key CALL set_table_property('dws_user_behaviour_rb', 'distribution_key', 'bucket'); END;
用戶屬性聚合結果表dim_userbase_rb。
--用戶屬性表的聚合結果表 BEGIN; CREATE TABLE IF NOT EXISTS dim_userbase_rb ( rb_uid roaringbitmap, -- UV計算 bucket int NOT NULL, -- 分桶字段 age text, gender text, country text, prov text, city text, PRIMARY key(age,gender,country, prov,city,bucket)--查詢維度作為主鍵,防止重復插入數據 ); CALL set_table_property('dim_userbase_rb', 'orientation', 'column'); CALL set_table_property('dim_userbase_rb', 'distribution_key', 'bucket');--bucket設置成distribution key,可以利用local join的能力 COMMIT;
步驟二:構建RB導入至聚合表
準備好明細表后,需要將明細結果表數據寫入聚合結果表,同時也需要將UID進行分桶存入Bucket字段中。分桶的劃分需要根據業務的數據規模、數據分布特征綜合考慮,本示例中將UID劃分為256個桶,這樣可以將UID充分打散到每個Shard,系統也會保證同一個桶的UID數據分布在相同的Shard,查詢時并行計算,實現高性能查詢。最終的導入命令如下:
行為表數據寫入聚合表:
--明細表數據寫入聚合表 INSERT INTO dws_user_behaviour_rb SELECT RB_BUILD_AGG(uid), uid >> 24 AS bucket,--右移24位,這樣高8位作為桶,低24為作為bitmap operator, channel, shop_id, time ymd FROM ods_user_behaviour_detail WHERE ymd >= '20231201' AND ymd <='20240503'
屬性表數據寫入聚合結果表:
--屬性表數據寫入聚合結果表 INSERT INTO dim_userbase_rb SELECT RB_BUILD_AGG(uid), uid >> 24 AS bucket,--右移24位,這樣高8位作為桶,低24為作為bitmap age, gender, country, prov, city FROM dim_userbase GROUP BY age,gender,country,prov,city,bucket;
當明細表更新后,根據明細表的更新粒度,聚合表的更新可以分為增量更新或者全量更新。二者區別如下:
增量更新聚合表:明細表數據有規律地更新或新增,例如只更新昨天的分區,可以通過INSERT的方式將增量數據寫入聚合表。
全量更新聚合表:明細表的數據無規律地更新,無法快速計算出增量數據,導致無法用新增數據更新聚合表,因此使用全量回刷方式寫入聚合表。
步驟三:任意長周期UV查詢
查詢時,可以通過聚合表查詢基礎維度任意組合任意時間段的UV、PV,查詢效率相比傳統SQL更高。示例如下:
--查詢:gender = 'man'&country = '北京'&operator = '購買'&shop_id ='1'的客群數
SELECT
SUM(RB_CARDINALITY (rb_and (t1.rb_uid, t2.rb_uid)))
FROM (
SELECT
rb_or_agg (rb_uid) AS rb_uid,
bucket
FROM
dws_user_behaviour_rb
WHERE
OPERATOR = '購買'
AND shop_id = '1'
AND ymd = '20240501'
GROUP BY
bucket) t1
JOIN (
SELECT
rb_or_agg (rb_uid) AS rb_uid,
bucket
FROM
dim_userbase_rb
WHERE
gender = 'man'
AND country = '北京'
GROUP BY
bucket) t2 ON t1.bucket = t2.bucket;