main pico / sql / migrations / 20260503_add_analytics_summary_tables.sql
Eric Bower  ·  2026-05-03
 1-- analytics_user_sites: tracks every site a user has had traffic on
 2CREATE TABLE IF NOT EXISTS analytics_user_sites (
 3  id serial NOT NULL,
 4  user_id uuid NOT NULL,
 5  host character varying(253) NOT NULL,
 6  total_visits integer NOT NULL DEFAULT 0,
 7  last_seen date NOT NULL,
 8  created_at timestamp without time zone NOT NULL DEFAULT now(),
 9  updated_at timestamp without time zone NOT NULL DEFAULT now(),
10  CONSTRAINT analytics_user_sites_pkey PRIMARY KEY (id),
11  CONSTRAINT analytics_user_sites_unique UNIQUE (user_id, host),
12  CONSTRAINT fk_user_sites_app_users
13    FOREIGN KEY(user_id)
14  REFERENCES app_users(id)
15  ON DELETE CASCADE
16  ON UPDATE CASCADE
17);
18
19CREATE INDEX IF NOT EXISTS idx_user_sites_user ON analytics_user_sites (user_id);
20
21-- analytics_monthly_visits: daily unique visitor counts per host with device breakdown
22CREATE TABLE IF NOT EXISTS analytics_monthly_visits (
23  id serial NOT NULL,
24  user_id uuid NOT NULL,
25  host character varying(253) NOT NULL,
26  visit_date date NOT NULL,
27  unique_visits integer NOT NULL DEFAULT 0,
28  mobile_visits integer NOT NULL DEFAULT 0,
29  desktop_visits integer NOT NULL DEFAULT 0,
30  created_at timestamp without time zone NOT NULL DEFAULT now(),
31  CONSTRAINT analytics_monthly_visits_pkey PRIMARY KEY (id),
32  CONSTRAINT analytics_monthly_visits_unique UNIQUE (user_id, host, visit_date),
33  CONSTRAINT fk_monthly_visits_app_users
34    FOREIGN KEY(user_id)
35  REFERENCES app_users(id)
36  ON DELETE CASCADE
37  ON UPDATE CASCADE
38);
39
40CREATE INDEX IF NOT EXISTS idx_monthly_visits_user_host ON analytics_monthly_visits (user_id, host);
41CREATE INDEX IF NOT EXISTS idx_monthly_visits_user_date ON analytics_monthly_visits (user_id, visit_date DESC);
42
43-- analytics_monthly_top_urls: top URLs per host per month per status code
44CREATE TABLE IF NOT EXISTS analytics_monthly_top_urls (
45  id serial NOT NULL,
46  user_id uuid NOT NULL,
47  host character varying(253) NOT NULL,
48  month date NOT NULL,
49  path character varying(2048) NOT NULL,
50  unique_visits integer NOT NULL DEFAULT 0,
51  status_code integer NOT NULL,
52  rank integer NOT NULL,
53  created_at timestamp without time zone NOT NULL DEFAULT now(),
54  CONSTRAINT analytics_monthly_top_urls_pkey PRIMARY KEY (id),
55  CONSTRAINT analytics_monthly_top_urls_unique UNIQUE (user_id, host, month, path, status_code),
56  CONSTRAINT fk_monthly_top_urls_app_users
57    FOREIGN KEY(user_id)
58  REFERENCES app_users(id)
59  ON DELETE CASCADE
60  ON UPDATE CASCADE
61);
62
63CREATE INDEX IF NOT EXISTS idx_monthly_top_urls_user_host_month ON analytics_monthly_top_urls (user_id, host, month);
64CREATE INDEX IF NOT EXISTS idx_monthly_top_urls_status ON analytics_monthly_top_urls (user_id, host, month, status_code, rank);
65
66-- analytics_monthly_top_referers: top referers per host per month
67CREATE TABLE IF NOT EXISTS analytics_monthly_top_referers (
68  id serial NOT NULL,
69  user_id uuid NOT NULL,
70  host character varying(253) NOT NULL,
71  month date NOT NULL,
72  referer character varying(253) NOT NULL,
73  unique_visits integer NOT NULL DEFAULT 0,
74  rank integer NOT NULL,
75  created_at timestamp without time zone NOT NULL DEFAULT now(),
76  CONSTRAINT analytics_monthly_top_referers_pkey PRIMARY KEY (id),
77  CONSTRAINT analytics_monthly_top_referers_unique UNIQUE (user_id, host, month, referer),
78  CONSTRAINT fk_monthly_top_referers_app_users
79    FOREIGN KEY(user_id)
80  REFERENCES app_users(id)
81  ON DELETE CASCADE
82  ON UPDATE CASCADE
83);
84
85CREATE INDEX IF NOT EXISTS idx_monthly_referers_user_host_month ON analytics_monthly_top_referers (user_id, host, month);