← Back to Dimensions

Customer Loyalty Tier

Also known as: Loyalty Tier, Loyalty Status, Loyalty Membership Tier, Membership Tier, Member Tier, Rewards Tier, Loyalty Level

Retentiondimensione-commerceloyaltyprogram-native-tierTravel & HospitalityE-commerce

Classifies each customer once per reporting date by the native tier of the governed primary loyalty program. Effective-dated membership history supplies the tier.

Business Use Case

1️⃣ Loyalty managers compare customer tier mix by program and investigate unexpected shifts. 2️⃣ Merchandising compares conversion after joining daily program-tier aggregates. 3️⃣ Finance reviews revenue and benefit cost by program tier before policy changes. 4️⃣ Data stewards investigate unknown and unmapped counts and repair source mappings.

Business Criticality

High

Journey / Navigation Area

Customer Loyalty

Owning Function

Marketing

Scope

User

Calculation Notes

1️⃣ Require one nonblank stable analytics key for each identified or anonymous customer. 2️⃣ Select one governed primary program per customer and reporting date. 3️⃣ Resolve primary-program ties by priority, effective start, source update, then record ID. 4️⃣ Derive not_enrolled, not_available, and unknown only from governed status and identity fields. 5️⃣ Normalize source and reference codes identically; map unmatched nonempty tiers to unmapped. 6️⃣ Return aggregate customer counts only; never expose customer identifiers.

Technical Details

Data Typestring
Lookback WindowDaily, Weekly, Monthly

Events

SQL Queries

Google Analytics 4 (BigQuery)
-- ==========================================
-- 1. GOOGLE BIGQUERY SQL
-- ==========================================
WITH reporting_dates AS (
  SELECT calendar_date AS snapshot_date
  FROM `your-project.reference.calendar_date`
  WHERE calendar_date BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 89 DAY) AND CURRENT_DATE()
),
source_rows AS (
  SELECT d.snapshot_date, NULLIF(TRIM(CAST(h.customer_analytics_key AS STRING)), '') AS customer_partition_key,
    h.identity_status, h.program_code, h.member_status, h.native_tier_code, h.is_primary_program,
    h.primary_program_priority, h.effective_from_date, h.source_updated_at, h.membership_record_id
  FROM reporting_dates d
  JOIN `your-project.crm.loyalty_membership_history` h
    ON d.snapshot_date >= h.effective_from_date
   AND (h.effective_to_date IS NULL OR d.snapshot_date < h.effective_to_date)
  WHERE NULLIF(TRIM(CAST(h.customer_analytics_key AS STRING)), '') IS NOT NULL
),
primary_membership_candidates AS (
  SELECT s.*, ROW_NUMBER() OVER (
    PARTITION BY snapshot_date, customer_partition_key
    ORDER BY CASE WHEN COALESCE(is_primary_program, FALSE) THEN 0 ELSE 1 END,
      COALESCE(primary_program_priority, 2147483647), effective_from_date DESC, source_updated_at DESC, membership_record_id DESC
  ) AS primary_rank
  FROM source_rows s
),
normalized AS (
  SELECT snapshot_date, customer_partition_key,
    REGEXP_REPLACE(LOWER(TRIM(identity_status)), r'[ -]+', '_') AS identity_status,
    REGEXP_REPLACE(LOWER(TRIM(program_code)), r'[ -]+', '_') AS program_code,
    REGEXP_REPLACE(LOWER(TRIM(member_status)), r'[ -]+', '_') AS member_status,
    REGEXP_REPLACE(LOWER(TRIM(native_tier_code)), r'[ -]+', '_') AS native_tier_code
  FROM primary_membership_candidates
  WHERE primary_rank = 1
),
normalized_reference AS (
  SELECT REGEXP_REPLACE(LOWER(TRIM(program_code)), r'[ -]+', '_') AS program_code,
    REGEXP_REPLACE(LOWER(TRIM(source_tier_code)), r'[ -]+', '_') AS source_tier_code,
    REGEXP_REPLACE(LOWER(TRIM(approved_native_tier_code)), r'[ -]+', '_') AS approved_native_tier_code,
    effective_from_date, effective_to_date, source_updated_at, mapping_id
  FROM `your-project.reference.loyalty_native_tier_reference`
),
mapping_candidates AS (
  SELECT n.*, r.approved_native_tier_code, ROW_NUMBER() OVER (
    PARTITION BY n.snapshot_date, n.customer_partition_key
    ORDER BY r.effective_from_date DESC, r.source_updated_at DESC, r.mapping_id DESC
  ) AS mapping_rank
  FROM normalized n
  LEFT JOIN normalized_reference r
    ON n.program_code = r.program_code
   AND n.native_tier_code = r.source_tier_code
   AND n.snapshot_date >= r.effective_from_date
   AND (r.effective_to_date IS NULL OR n.snapshot_date < r.effective_to_date)
),
classified AS (
  SELECT snapshot_date, program_code, member_status,
    CASE
      WHEN identity_status = 'anonymous' THEN 'not_available'
      WHEN member_status IN ('not_enrolled', 'non_member') THEN 'not_enrolled'
      WHEN member_status IN ('unknown', 'unresolved') THEN 'unknown'
      WHEN NULLIF(native_tier_code, '') IS NULL THEN 'unknown'
      WHEN approved_native_tier_code IS NULL THEN 'unmapped'
      ELSE approved_native_tier_code
    END AS customer_loyalty_tier
  FROM mapping_candidates
  WHERE mapping_rank = 1
)
SELECT snapshot_date, program_code, member_status, customer_loyalty_tier, COUNT(*) AS customer_count
FROM classified
GROUP BY snapshot_date, program_code, member_status, customer_loyalty_tier
ORDER BY snapshot_date, program_code, customer_loyalty_tier;
Adobe Experience Platform (AEP Query Service)
-- ==========================================
-- 2. ADOBE AEP QUERY SERVICE SQL
-- ==========================================
WITH reporting_dates AS (
  SELECT calendar_date AS snapshot_date
  FROM enterprise_calendar
  WHERE calendar_date BETWEEN CURRENT_DATE - INTERVAL '89' DAY AND CURRENT_DATE
),
source_rows AS (
  SELECT d.snapshot_date, NULLIF(TRIM(CAST(h.customer_analytics_key AS STRING)), '') AS customer_partition_key,
    h.identity_status, h.program_code, h.member_status, h.native_tier_code, h.is_primary_program,
    h.primary_program_priority, h.effective_from_date, h.source_updated_at, h.membership_record_id
  FROM reporting_dates d
  JOIN loyalty_membership_history h
    ON d.snapshot_date >= h.effective_from_date
   AND (h.effective_to_date IS NULL OR d.snapshot_date < h.effective_to_date)
  WHERE NULLIF(TRIM(CAST(h.customer_analytics_key AS STRING)), '') IS NOT NULL
),
primary_membership_candidates AS (
  SELECT s.*, ROW_NUMBER() OVER (
    PARTITION BY snapshot_date, customer_partition_key
    ORDER BY CASE WHEN COALESCE(is_primary_program, FALSE) THEN 0 ELSE 1 END,
      COALESCE(primary_program_priority, 2147483647), effective_from_date DESC, source_updated_at DESC, membership_record_id DESC
  ) AS primary_rank
  FROM source_rows s
),
normalized AS (
  SELECT snapshot_date, customer_partition_key,
    REGEXP_REPLACE(LOWER(TRIM(identity_status)), '[ -]+', '_') AS identity_status,
    REGEXP_REPLACE(LOWER(TRIM(program_code)), '[ -]+', '_') AS program_code,
    REGEXP_REPLACE(LOWER(TRIM(member_status)), '[ -]+', '_') AS member_status,
    REGEXP_REPLACE(LOWER(TRIM(native_tier_code)), '[ -]+', '_') AS native_tier_code
  FROM primary_membership_candidates
  WHERE primary_rank = 1
),
normalized_reference AS (
  SELECT REGEXP_REPLACE(LOWER(TRIM(program_code)), '[ -]+', '_') AS program_code,
    REGEXP_REPLACE(LOWER(TRIM(source_tier_code)), '[ -]+', '_') AS source_tier_code,
    REGEXP_REPLACE(LOWER(TRIM(approved_native_tier_code)), '[ -]+', '_') AS approved_native_tier_code,
    effective_from_date, effective_to_date, source_updated_at, mapping_id
  FROM loyalty_native_tier_reference
),
mapping_candidates AS (
  SELECT n.*, r.approved_native_tier_code, ROW_NUMBER() OVER (
    PARTITION BY n.snapshot_date, n.customer_partition_key
    ORDER BY r.effective_from_date DESC, r.source_updated_at DESC, r.mapping_id DESC
  ) AS mapping_rank
  FROM normalized n
  LEFT JOIN normalized_reference r
    ON n.program_code = r.program_code
   AND n.native_tier_code = r.source_tier_code
   AND n.snapshot_date >= r.effective_from_date
   AND (r.effective_to_date IS NULL OR n.snapshot_date < r.effective_to_date)
),
classified AS (
  SELECT snapshot_date, program_code, member_status,
    CASE
      WHEN identity_status = 'anonymous' THEN 'not_available'
      WHEN member_status IN ('not_enrolled', 'non_member') THEN 'not_enrolled'
      WHEN member_status IN ('unknown', 'unresolved') THEN 'unknown'
      WHEN native_tier_code IS NULL OR native_tier_code = '' THEN 'unknown'
      WHEN approved_native_tier_code IS NULL THEN 'unmapped'
      ELSE approved_native_tier_code
    END AS customer_loyalty_tier
  FROM mapping_candidates
  WHERE mapping_rank = 1
)
SELECT snapshot_date, program_code, member_status, customer_loyalty_tier, COUNT(*) AS customer_count
FROM classified
GROUP BY snapshot_date, program_code, member_status, customer_loyalty_tier
ORDER BY snapshot_date, program_code, customer_loyalty_tier;

Source Data

CRM

Dependencies

Prerequisite: List the dependencies required for this Dimension

Report Attributes

Snapshot Date Primary Loyalty Program Customer Loyalty Tier Loyalty Member Status Customer Status

Segment Eligibility

true

Governance

Data SensitivityRestricted
Contains PIIYes - This Dimension contains Personally Identifiable Information

Governance

Created bydevyendarm
Created on9/9/2026
Last modified bydevyendarm
Last modified on9/29/2026
Version1.0.0
Validation stateqa passed
StatusPUBLISHED

GitHub

View File on GitHubcatalog/dimensions/customer-loyalty-tier.yml

Community Discussion