Group Bitmap User Overlap Analysis: Practical Guide


Why Choose Bitmap?

Analysis scenarioTraditional SQL approachBitmap approachPerformance comparison (100M users)
Funnel conversionMulti-table JOIN + COUNT DISTINCTbitmap_and + bitmap_cardinalityTraditional: ~30s / Bitmap: <0.1s
Retention analysisSelf-join + date filterGenerate Bitmap per day, bitwise ANDTraditional: ~15s / Bitmap: <0.1s
Multi-dimensional segmentationDynamic WHERE condition aggregationPre-compute tag Bitmaps, real-time bitwise opsTraditional: ~5s / Bitmap: <0.05s
A/B test isolationINTERSECT or IN subquerybitmap_and to check if cardinality is 0Traditional: ~2s / Bitmap: <0.01s

Core Function Reference

FunctionPurposeReturn typeUse case
group_bitmap_stateGenerate a user Bitmap objectBITMAPPre-compute user sets by tag/channel/step
bitmap_andCompute intersectionBITMAPFunnel conversion, retained users, audience intersection
bitmap_orCompute unionBITMAPTotal reach, audience merge
bitmap_xorCompute symmetric differenceBITMAPNew users, churned users, A/B isolation check
bitmap_cardinalityCount elements in a BitmapBIGINTGet final unique user count (UV)

Prerequisites

-- User behavior event log table CREATE TABLE user_events ( user_id BIGINT, event_name VARCHAR(50), event_date DATE, channel VARCHAR(20) ); -- Sample funnel data: view -> cart -> pay INSERT INTO user_events VALUES (1, 'view', CAST('2026-05-01' AS DATE), 'app'), (1, 'cart', CAST('2026-05-01' AS DATE), 'app'), (1, 'pay', CAST('2026-05-01' AS DATE), 'app'), (2, 'view', CAST('2026-05-01' AS DATE), 'app'), (2, 'cart', CAST('2026-05-01' AS DATE), 'app'), (3, 'view', CAST('2026-05-01' AS DATE), 'app'), (4, 'view', CAST('2026-05-01' AS DATE), 'web'), (4, 'cart', CAST('2026-05-01' AS DATE), 'web'), (4, 'pay', CAST('2026-05-01' AS DATE), 'web'), (5, 'view', CAST('2026-05-02' AS DATE), 'app'), (5, 'cart', CAST('2026-05-02' AS DATE), 'app'), (1, 'view', CAST('2026-05-02' AS DATE), 'app'), (1, 'pay', CAST('2026-05-02' AS DATE), 'app');


Scenario 1: Funnel Conversion Analysis (Sub-second)

Problem

Calculate the conversion rate for "view -> cart -> pay". Traditional SQL requires multiple JOINs, which easily times out on large datasets.

Bitmap Implementation

WITH funnel_steps AS ( SELECT -- Generate a Bitmap for each funnel step group_bitmap_state(CASE WHEN event_name = 'view' THEN user_id END) AS step_view, group_bitmap_state(CASE WHEN event_name = 'cart' THEN user_id END) AS step_cart, group_bitmap_state(CASE WHEN event_name = 'pay' THEN user_id END) AS step_pay FROM user_events WHERE event_date = CAST('2026-05-01' AS DATE) ) SELECT bitmap_cardinality(step_view) AS view_uv, bitmap_cardinality(step_cart) AS cart_uv, bitmap_cardinality(step_pay) AS pay_uv, -- Conversion rate: current step count / previous step count ROUND(bitmap_cardinality(step_cart) * 100.0 / bitmap_cardinality(step_view), 2) AS view_to_cart_rate, ROUND(bitmap_cardinality(step_pay) * 100.0 / bitmap_cardinality(step_cart), 2) AS cart_to_pay_rate FROM funnel_steps;

Output:

view_uvcart_uvpay_uvview_to_cart_ratecart_to_pay_rate
43275.0066.67

Scenario 2: Retained User Analysis (N-day Retention)

Problem

Calculate how many users active on May 1 were also active on May 2 and May 3.

Bitmap Implementation

WITH daily_users AS ( -- Aggregate by day to generate Bitmaps SELECT event_date, group_bitmap_state(user_id) AS daily_bm FROM user_events GROUP BY event_date ), base_day AS ( SELECT daily_bm AS base_bm FROM daily_users WHERE event_date = CAST('2026-05-01' AS DATE) ) SELECT d.event_date, bitmap_cardinality(d.daily_bm) AS daily_uv, -- Retention: intersection of the current day's Bitmap with the baseline day's Bitmap bitmap_cardinality(bitmap_and(d.daily_bm, b.base_bm)) AS retained_uv, ROUND(bitmap_cardinality(bitmap_and(d.daily_bm, b.base_bm)) * 100.0 / bitmap_cardinality(b.base_bm), 2) AS retention_rate FROM daily_users d CROSS JOIN base_day b WHERE d.event_date >= CAST('2026-05-01' AS DATE) ORDER BY d.event_date;

Output:

event_datedaily_uvretained_uvretention_rate
2026-05-0144100.00
2026-05-022125.00

Scenario 3: Real-time Multi-dimensional Audience Segmentation (Ad-Hoc Query)

Problem

A marketer needs to query in real time: "How many users came from the App channel and made a payment?"

Bitmap Implementation

Step 1: Pre-compute tag Bitmaps (Dynamic Table or scheduled job)

-- Example: pre-compute Bitmaps by channel and event type CREATE TABLE tag_bitmaps AS SELECT channel, event_name, group_bitmap_state(user_id) AS bm FROM user_events GROUP BY channel, event_name;

Step 2: Real-time query (millisecond response)

-- Query: users from the App channel who also made a payment -- Logic: (Bitmap of all App users) AND (Bitmap of all paying users) WITH app_users AS ( SELECT group_bitmap_state(user_id) AS bm FROM user_events WHERE channel = 'app' ), pay_users AS ( SELECT group_bitmap_state(user_id) AS bm FROM user_events WHERE event_name = 'pay' ) SELECT bitmap_cardinality(bitmap_and(a.bm, b.bm)) AS target_uv FROM app_users a, pay_users b;

Output:

target_uv
2

Scenario 4: A/B Test Audience Isolation Check

Problem

Verify that the control group and treatment group in an A/B test do not share any users (overlap contaminates experiment results).

Bitmap Implementation

WITH groups AS ( SELECT group_bitmap_state(CASE WHEN group_name = 'control' THEN user_id END) AS control_bm, group_bitmap_state(CASE WHEN group_name = 'treatment' THEN user_id END) AS treatment_bm FROM ab_test_users ) SELECT bitmap_cardinality(bitmap_and(control_bm, treatment_bm)) AS overlap_count, CASE WHEN bitmap_cardinality(bitmap_and(control_bm, treatment_bm)) > 0 THEN '⚠️ Overlap detected — experiment invalid' ELSE '✅ Audiences are isolated — experiment valid' END AS check_result FROM groups;

Output:

overlap_countcheck_result
0✅ Audiences are isolated — experiment valid

Common Issues

1. group_bitmap vs group_bitmap_state

-- Wrong: group_bitmap returns a cardinality (INT) and cannot be used in bitwise operations SELECT bitmap_and(group_bitmap(user_id), ...) -- error -- Correct: use group_bitmap_state to produce a Bitmap object SELECT bitmap_and(group_bitmap_state(user_id), ...)

2. Handling negative IDs

-- Bitmap only supports non-negative integers. If user_id contains negatives or strings, convert first. -- Wrong: group_bitmap_state(user_id) WHERE user_id = -1 -- Correct: ensure ID >= 0, or use a hash function to convert to a positive integer SELECT group_bitmap_state(abs(hash(user_id))) FROM users;

3. Memory limits

-- A single Bitmap object is usually small in memory (compressed), but in extreme cases -- (e.g., the full user base) it may consume significant memory. -- Recommendation: shard Bitmaps by time or business domain to avoid oversized single Bitmaps.


Performance Optimization Tips

ScenarioOptimization strategy
High-frequency queriesStore Bitmap results in a Dynamic Table with REFRESH INTERVAL 1 HOUR for automatic updates
Storage optimizationStore Bitmap columns using the BITMAP type; Singdata Lakehouse automatically applies RoaringBitmap compression
Query accelerationBuild a Bloom Filter index on Bitmap columns to speed up bitmap_cardinality queries

-- Recommended architecture: ODS -> DWD (detail) -> DWS (Bitmap aggregation) -> ADS (application queries) CREATE DYNAMIC TABLE dws_user_bitmaps REFRESH INTERVAL 1 HOUR AS SELECT DATE_TRUNC('DAY', event_time) AS event_date, channel, group_bitmap_state(user_id) AS user_bm FROM dwd_user_events GROUP BY 1, 2;