-- 13_crq_columns.sql — fleettickets · unpack tickets.crq.raw into typed columns -- ───────────────────────────────────────────────────────────────────────────── -- CRQ (new-installation) mirror of 03_inc_columns.sql. CRQ shares the INC source's -- IDENTICAL 32-column flat-CSV schema, so the same STORED generated columns apply: -- the crq dataset gets real typed, indexable columns while `raw` stays the source -- of truth (drift-safe). STORED generated columns are computed for ALL existing -- rows on creation and auto-recomputed on every future insert/update — no loader -- change needed. -- -- tickets.eat_ts() (EAT wall-clock text -> timestamptz, IMMUTABLE) already exists -- from 03_inc_columns.sql — reuse it, don't redefine. See that file's note on why -- IMMUTABLE is safe for Kenya (fixed UTC+3, no DST). -- -- Idempotent: safe on a fresh DB and re-appliable on the live DB. -- ───────────────────────────────────────────────────────────────────────────── SET search_path = tickets, public; ALTER TABLE tickets.crq -- text ADD COLUMN IF NOT EXISTS service_type text GENERATED ALWAYS AS (raw->>'service_type') STORED, ADD COLUMN IF NOT EXISTS bucket text GENERATED ALWAYS AS (raw->>'bucket') STORED, ADD COLUMN IF NOT EXISTS raw_status text GENERATED ALWAYS AS (raw->>'raw_status') STORED, ADD COLUMN IF NOT EXISTS normalized_status text GENERATED ALWAYS AS (raw->>'normalized_status') STORED, ADD COLUMN IF NOT EXISTS cluster text GENERATED ALWAYS AS (raw->>'cluster') STORED, ADD COLUMN IF NOT EXISTS region text GENERATED ALWAYS AS (raw->>'region') STORED, ADD COLUMN IF NOT EXISTS location_name text GENERATED ALWAYS AS (raw->>'location_name') STORED, ADD COLUMN IF NOT EXISTS assigned_team text GENERATED ALWAYS AS (raw->>'assigned_team') STORED, ADD COLUMN IF NOT EXISTS owner text GENERATED ALWAYS AS (raw->>'owner') STORED, ADD COLUMN IF NOT EXISTS sla_status text GENERATED ALWAYS AS (raw->>'sla_status') STORED, -- numeric / float ADD COLUMN IF NOT EXISTS mttr numeric GENERATED ALWAYS AS (NULLIF(raw->>'mttr','')::numeric) STORED, ADD COLUMN IF NOT EXISTS latitude double precision GENERATED ALWAYS AS (NULLIF(raw->>'latitude','')::double precision) STORED, ADD COLUMN IF NOT EXISTS longitude double precision GENERATED ALWAYS AS (NULLIF(raw->>'longitude','')::double precision) STORED, -- boolean ADD COLUMN IF NOT EXISTS is_actionable boolean GENERATED ALWAYS AS (NULLIF(raw->>'is_actionable','')::boolean) STORED, ADD COLUMN IF NOT EXISTS is_auto_created boolean GENERATED ALWAYS AS (NULLIF(raw->>'is_auto_created','')::boolean) STORED, ADD COLUMN IF NOT EXISTS is_auto_closed boolean GENERATED ALWAYS AS (NULLIF(raw->>'is_auto_closed','')::boolean) STORED, ADD COLUMN IF NOT EXISTS is_alarm boolean GENERATED ALWAYS AS (NULLIF(raw->>'is_alarm','')::boolean) STORED, -- timestamps (EAT wall-clock -> timestamptz). created_at/updated_at are the -- EXPORT pipeline's bookkeeping (not ticket lifecycle), hence the source_ prefix. ADD COLUMN IF NOT EXISTS created_at_service timestamptz GENERATED ALWAYS AS (tickets.eat_ts(raw->>'created_at_service')) STORED, ADD COLUMN IF NOT EXISTS scheduled_at timestamptz GENERATED ALWAYS AS (tickets.eat_ts(raw->>'scheduled_at')) STORED, ADD COLUMN IF NOT EXISTS closed_at timestamptz GENERATED ALWAYS AS (tickets.eat_ts(raw->>'closed_at')) STORED, ADD COLUMN IF NOT EXISTS last_seen_at timestamptz GENERATED ALWAYS AS (tickets.eat_ts(raw->>'last_seen_at')) STORED, ADD COLUMN IF NOT EXISTS first_seen_at timestamptz GENERATED ALWAYS AS (tickets.eat_ts(raw->>'first_seen_at')) STORED, ADD COLUMN IF NOT EXISTS source_created_at timestamptz GENERATED ALWAYS AS (tickets.eat_ts(raw->>'created_at')) STORED, ADD COLUMN IF NOT EXISTS source_updated_at timestamptz GENERATED ALWAYS AS (tickets.eat_ts(raw->>'updated_at')) STORED; -- indexes on the new typed columns (serve cluster / team / closure queries) CREATE INDEX IF NOT EXISTS ix_crq_norm_status_col ON tickets.crq (normalized_status); CREATE INDEX IF NOT EXISTS ix_crq_cluster_col ON tickets.crq (cluster); CREATE INDEX IF NOT EXISTS ix_crq_assigned_team ON tickets.crq (assigned_team); CREATE INDEX IF NOT EXISTS ix_crq_closed_at ON tickets.crq (closed_at); CREATE INDEX IF NOT EXISTS ix_crq_actionable_col ON tickets.crq (is_actionable) WHERE is_actionable;