← back to Commercialrealestate

scripts/db/migrations/20260731_broker_firm_history.sql

78 lines

-- Migration: broker_firm_history
-- Tracks a broker's tenure at each firm (current + past).
-- Idempotent: safe to re-run at any time.
-- Run with:
--   psql -h /tmp -d cre -f scripts/db/migrations/20260731_broker_firm_history.sql
-- On Kamatera (Steve-gated deploy):
--   psql -h /tmp -U <user> -d cre -f scripts/db/migrations/20260731_broker_firm_history.sql

BEGIN;

-- ── Table ─────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS broker_firm_history (
  id          serial        PRIMARY KEY,
  broker_id   int           NOT NULL REFERENCES broker(id) ON DELETE CASCADE,
  firm_id     int           REFERENCES firm(id),
  firm_name   text,                          -- denorm for rows where firm is not in firm table
  title       text,
  start_date  date,
  end_date    date,
  is_current  boolean       NOT NULL DEFAULT false,
  source      text,
  source_url  text,
  tier        int,
  found_at    timestamptz   DEFAULT now()
);

-- ── Indexes ───────────────────────────────────────────────────────────────────
CREATE INDEX IF NOT EXISTS idx_bfh_broker  ON broker_firm_history(broker_id);
CREATE INDEX IF NOT EXISTS idx_bfh_firm    ON broker_firm_history(firm_id);

-- ── Uniqueness guard (prevents duplicate backfill rows) ───────────────────────
-- One active (is_current=true) row per broker+firm, plus any number of past rows
-- identified by start_date. NULLs in a UNIQUE index are not equal in PG, so
-- past rows without a start_date would collide; use a partial unique index for
-- the current row only — that's all we need to make the backfill idempotent.
CREATE UNIQUE INDEX IF NOT EXISTS idx_bfh_current_uniq
  ON broker_firm_history(broker_id, firm_id)
  WHERE is_current = true;

-- firm_id can be NULL for enriched rows whose firm isn't in the firm table yet.
-- NULLs are distinct in a UNIQUE index, so the guard above does NOT stop duplicate
-- current rows for the same broker+firm when firm_id is NULL — cover that by firm_name.
CREATE UNIQUE INDEX IF NOT EXISTS idx_bfh_current_nullfirm_uniq
  ON broker_firm_history(broker_id, firm_name)
  WHERE is_current = true AND firm_id IS NULL;

-- ── Backfill: seed current-firm rows from broker table ────────────────────────
-- Every broker with a firm_id gets one is_current=true row.
-- ON CONFLICT DO NOTHING makes this re-runnable.
INSERT INTO broker_firm_history (broker_id, firm_id, firm_name, is_current, source)
SELECT
  b.id          AS broker_id,
  b.firm_id,
  f.name        AS firm_name,
  true          AS is_current,
  'backfill'    AS source
FROM broker b
JOIN firm f ON f.id = b.firm_id
WHERE b.firm_id IS NOT NULL
ON CONFLICT DO NOTHING;

-- ── Grants (hygiene; the app connects as the table owner, this is for dw_admin) ─
DO $$ BEGIN
  IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'dw_admin') THEN
    GRANT SELECT, INSERT, UPDATE, DELETE ON broker_firm_history TO dw_admin;
    GRANT USAGE, SELECT ON SEQUENCE broker_firm_history_id_seq TO dw_admin;
  END IF;
END $$;

COMMIT;

-- ── Row-count report (run after commit) ──────────────────────────────────────
SELECT
  count(*)                               AS total_rows,
  count(*) FILTER (WHERE is_current)    AS current_rows,
  count(*) FILTER (WHERE NOT is_current) AS past_rows
FROM broker_firm_history;