-- A materialized view: aggregate per customer
DROPMATERIALIZEDVIEWIFEXISTScustomer_spend_mv;CREATEMATERIALIZEDVIEWcustomer_spend_mvASSELECTc.idAScustomer_id,c.nameAScustomer_name,count(o.id)ASorder_count,COALESCE(sum(o.total),0)AStotal_spent,max(o.id)ASlast_order_idFROMcustomers_examplecLEFTJOINorders_exampleoONo.customer_id=c.idGROUPBYc.id,c.name;-- Query it like a table
SELECT*FROMcustomer_spend_mvORDERBYtotal_spentDESC;-- Index it (a matview is a real table, so it supports indexes)
CREATEUNIQUEINDEXcustomer_spend_mv_pkONcustomer_spend_mv(customer_id);CREATEINDEXcustomer_spend_mv_total_idxONcustomer_spend_mv(total_spent);-- Add another order, then refresh
INSERTINTOorders_exampleVALUES(200,1,25.00);-- Plain REFRESH locks the matview against reads for the duration
REFRESHMATERIALIZEDVIEWcustomer_spend_mv;-- CONCURRENTLY lets readers continue. Requires a UNIQUE index on the matview.
REFRESHMATERIALIZEDVIEWCONCURRENTLYcustomer_spend_mv;SELECT*FROMcustomer_spend_mvORDERBYtotal_spentDESC;-- Find matviews and when they were last refreshed (PostgreSQL keeps the relation modification time)
SELECTschemaname,matviewname,hasindexes,ispopulatedFROMpg_matviewsWHEREschemaname='public';DROPMATERIALIZEDVIEWIFEXISTScustomer_spend_mv;