db: ClickHouse manar database — 11 analytical tables
This commit is contained in:
@@ -0,0 +1,212 @@
|
|||||||
|
-- ClickHouse: manar database + analytical tables
|
||||||
|
-- Run against existing ClickHouse container
|
||||||
|
-- All tables partitioned by brand_id for multi-tenant isolation
|
||||||
|
|
||||||
|
CREATE DATABASE IF NOT EXISTS manar;
|
||||||
|
|
||||||
|
-- ── Ad Metrics Hourly ────────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.ad_metrics_hourly (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
campaign_id String,
|
||||||
|
campaign_name String,
|
||||||
|
adgroup_id String,
|
||||||
|
adgroup_name String,
|
||||||
|
hour DateTime,
|
||||||
|
date Date,
|
||||||
|
impressions UInt64,
|
||||||
|
clicks UInt64,
|
||||||
|
cost_micros UInt64,
|
||||||
|
conversions Float64,
|
||||||
|
conv_value Float64,
|
||||||
|
video_views UInt64,
|
||||||
|
ctr Float64 MATERIALIZED if(impressions > 0, clicks / impressions, 0),
|
||||||
|
cpc_micros UInt64 MATERIALIZED if(clicks > 0, cost_micros / clicks, 0),
|
||||||
|
roas Float64 MATERIALIZED if(cost_micros > 0, conv_value / (cost_micros / 1000000), 0)
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, platform, account_id, campaign_id, hour)
|
||||||
|
TTL date + INTERVAL 2 YEAR;
|
||||||
|
|
||||||
|
-- ── Search Term Performance ──────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.search_term_performance (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
campaign_id String,
|
||||||
|
adgroup_id String,
|
||||||
|
date Date,
|
||||||
|
search_term String,
|
||||||
|
match_type String,
|
||||||
|
impressions UInt64,
|
||||||
|
clicks UInt64,
|
||||||
|
cost_micros UInt64,
|
||||||
|
conversions Float64,
|
||||||
|
quality_score UInt8
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, account_id, date, search_term)
|
||||||
|
TTL date + INTERVAL 1 YEAR;
|
||||||
|
|
||||||
|
-- ── Creative Performance ─────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.creative_performance (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
ad_id String,
|
||||||
|
ad_name String,
|
||||||
|
ad_type String,
|
||||||
|
date Date,
|
||||||
|
impressions UInt64,
|
||||||
|
clicks UInt64,
|
||||||
|
cost_micros UInt64,
|
||||||
|
conversions Float64,
|
||||||
|
headline String,
|
||||||
|
description String
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, account_id, ad_id, date)
|
||||||
|
TTL date + INTERVAL 1 YEAR;
|
||||||
|
|
||||||
|
-- ── Device Performance ───────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.device_performance (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
campaign_id String,
|
||||||
|
date Date,
|
||||||
|
device String,
|
||||||
|
impressions UInt64,
|
||||||
|
clicks UInt64,
|
||||||
|
cost_micros UInt64,
|
||||||
|
conversions Float64
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, account_id, campaign_id, date, device)
|
||||||
|
TTL date + INTERVAL 1 YEAR;
|
||||||
|
|
||||||
|
-- ── Geo Performance ──────────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.geo_performance (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
campaign_id String,
|
||||||
|
date Date,
|
||||||
|
country_code String,
|
||||||
|
region String,
|
||||||
|
city String,
|
||||||
|
impressions UInt64,
|
||||||
|
clicks UInt64,
|
||||||
|
cost_micros UInt64,
|
||||||
|
conversions Float64
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, account_id, campaign_id, date, country_code)
|
||||||
|
TTL date + INTERVAL 1 YEAR;
|
||||||
|
|
||||||
|
-- ── Day Part Performance ─────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.day_part_performance (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
campaign_id String,
|
||||||
|
date Date,
|
||||||
|
hour_of_day UInt8,
|
||||||
|
day_of_week UInt8,
|
||||||
|
impressions UInt64,
|
||||||
|
clicks UInt64,
|
||||||
|
cost_micros UInt64,
|
||||||
|
conversions Float64
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, account_id, campaign_id, date, hour_of_day)
|
||||||
|
TTL date + INTERVAL 1 YEAR;
|
||||||
|
|
||||||
|
-- ── Placement Performance ────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.placement_performance (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
campaign_id String,
|
||||||
|
date Date,
|
||||||
|
placement String,
|
||||||
|
placement_type String,
|
||||||
|
impressions UInt64,
|
||||||
|
clicks UInt64,
|
||||||
|
cost_micros UInt64,
|
||||||
|
conversions Float64
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, account_id, campaign_id, date, placement)
|
||||||
|
TTL date + INTERVAL 1 YEAR;
|
||||||
|
|
||||||
|
-- ── Audience Segments ────────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.audience_segments (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
campaign_id String,
|
||||||
|
date Date,
|
||||||
|
audience_id String,
|
||||||
|
audience_name String,
|
||||||
|
audience_type String,
|
||||||
|
impressions UInt64,
|
||||||
|
clicks UInt64,
|
||||||
|
cost_micros UInt64,
|
||||||
|
conversions Float64
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, account_id, campaign_id, date, audience_id)
|
||||||
|
TTL date + INTERVAL 1 YEAR;
|
||||||
|
|
||||||
|
-- ── Anomaly Events ───────────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.anomaly_events (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
entity_type String,
|
||||||
|
entity_id String,
|
||||||
|
metric String,
|
||||||
|
detected_at DateTime DEFAULT now(),
|
||||||
|
date Date,
|
||||||
|
expected_value Float64,
|
||||||
|
actual_value Float64,
|
||||||
|
deviation_pct Float64,
|
||||||
|
severity String,
|
||||||
|
resolved UInt8 DEFAULT 0
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, detected_at)
|
||||||
|
TTL date + INTERVAL 6 MONTH;
|
||||||
|
|
||||||
|
-- ── Competitor Signals ───────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.competitor_signals (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
date Date,
|
||||||
|
keyword String,
|
||||||
|
competitor_domain String,
|
||||||
|
auction_insight_metric String,
|
||||||
|
value Float64
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, account_id, date, keyword, competitor_domain)
|
||||||
|
TTL date + INTERVAL 1 YEAR;
|
||||||
|
|
||||||
|
-- ── Conversion Path ──────────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS manar.conversion_path (
|
||||||
|
brand_id String,
|
||||||
|
platform String,
|
||||||
|
account_id String,
|
||||||
|
date Date,
|
||||||
|
conversion_name String,
|
||||||
|
path_length UInt8,
|
||||||
|
days_to_convert UInt16,
|
||||||
|
conversions Float64,
|
||||||
|
conv_value Float64
|
||||||
|
) ENGINE = MergeTree()
|
||||||
|
PARTITION BY (brand_id, toYYYYMM(date))
|
||||||
|
ORDER BY (brand_id, account_id, date, conversion_name)
|
||||||
|
TTL date + INTERVAL 1 YEAR;
|
||||||
Reference in New Issue
Block a user