When you create a materialized view, the engine executes the defining query and persists the output. Reading it afterward never touches the base tables, so there is no join or aggregation cost at query time.
Refreshing replaces or updates that stored data. A full refresh re-runs the whole query and swaps in the new result. PostgreSQL does exactly this with REFRESH MATERIALIZED VIEW, and the CONCURRENTLY option lets reads keep working against the old copy while the new one is built, at the cost of needing a unique index.
Incremental or fast refresh only applies the rows that changed since the last refresh, which is far cheaper for large views. Oracle supports this with materialized view logs that track base-table changes, and Snowflake and BigQuery maintain their materialized views automatically in the background. The trade-off is that incremental refresh imposes restrictions on what your query can contain.
Refresh is usually triggered one of three ways: on a fixed schedule through a cron job or database scheduler, on demand right after a known bulk load, or automatically by the engine when it detects base-table changes.