WITH cte AS (
SELECT d.thedate
, lead(m.label) OVER (PARTITION BY m.label, d.thedate, d.number
ORDER BY d.thetime) AS leader
FROM table_data d
LEFT JOIN table_mapper m USING (type)
WHERE thedate BETWEEN date_trunc('month', current_date - 1)
AND current_date - 1
)
SELECT 'MTD' AS label, round(count(leader)::numeric / count(*) * 100, 1) AS val
FROM cte
UNION ALL
SELECT 'Week', round(count(leader)::numeric / count(*) * 100, 1)
FROM cte
WHERE thedate BETWEEN date_trunc('week', current_date - 1) AND current_date - 1
UNION ALL
SELECT 'FTD', round(count(leader)::numeric / count(*) * 100, 1)
FROM cte
WHERE thedate = current_date - 1;
The CTE makes sense for big tables, so you only scan it once. For smaller tables it may be faster without ...
Using thedate
instead of reserved word date
(in standard SQL).
thetime
, uni
instead of time
, unique
. Etc.
Simplified the lead()
call. You get a value or NULL for the leading row. That seems the be the only relevant information.
It's a pointless waste to repeat columns from the PARTITION
clause in the ORDER BY
clause of a window function.
Building on that, count(leader) / count(*)
instead of sum(uni) / count(uni)
is a bit faster. count(column)
only counts non-null values, while count(*)
counts all rows.
The condition for the first term of the UNION
query was redundant.
More advice and links about data definition in the comments to the question.
Table design / Indexes
You should have primary keys. I suggest serial
or IDENTITY
column as surrogate PK for table_data
:
ALTER TABLE table_data ADD COLUMN table_data_id serial PRIMARY KEY;
See:
Make type
the primary key of table_mapper
(also needed for the following FK constraint):
ALTER TABLE table_mapper ADD CONSTRAINT table_mapper_pkey (type);
Add a foreign key constraint for type
to enforce referential integrity. Something like:
ALTER TABLE table_data ADD CONSTRAINT table_data_type_fkey
FOREIGN KEY (type) REFERENCES table_mapper (type)
ON UPDATE CASCADE ON DELETE NO ACTION;
For ultimate read performance (at some cost for writes), add a multi-column index to possibly allow index-only scans for above query:
CREATE INDEX table_data_foo_idx ON table_data (thedate, number, thetime);