Queries that use variable parameters — such as a date filter that changes with each execution — cannot match a fixed materialized view directly, because the view is built against a specific condition. The query rewrite feature for real-time materialized views solves this: it checks whether the actual query condition falls within the range defined in the view and, if so, rewrites the query to scan the view instead of the base table.
This document walks through how to design a real-time materialized view and enable query rewrite for a TPC-H Q1 query that uses a variable date filter.
This example is based on the TPC-H benchmark but does not meet all TPC-H benchmark requirements. Results may differ from published TPC-H benchmark results. For details about TPC-H Q1, see TPC-H.
When to use this approach
Use real-time materialized views with query rewrite for variable-parameter queries when all of the following are true:
The query runs frequently with different parameter values (for example, different date ranges).
The parameter values fall within a predictable range (for example, an interval between 60 and 120 days).
The query is resource-intensive on the base table.
You can accept the storage and maintenance cost of maintaining the materialized view.
If the parameter range is unbounded or unpredictable, query rewrite cannot apply and the query falls back to a full scan of the base table.
How query rewrite works for variable parameters
Query rewrite for variable parameters relies on two mechanisms.
Re-aggregation for supplement
In SQL, the WHERE clause executes before GROUP BY. If a query groups by l_returnflag and l_linestatus but filters on a variable l_shipdate, the materialized view must include l_shipdate in its GROUP BY clause. At query time, query rewrite applies the dynamic WHERE filter on the view and re-aggregates the results on l_returnflag and l_linestatus.
The AVG function does not support re-aggregation directly. Query rewrite handles this through the expression supplement mechanism: it stores SUM and COUNT in the materialized view separately, then computes AVG = SUM / COUNT at query time.
WHERE condition range matching
Query rewrite checks whether the WHERE condition in the actual query is a subset of the condition in the materialized view. If the view covers l_shipdate <= date '1998-12-01' - interval '60' day, any query with an interval between 60 and 120 days falls within that range. Query rewrite automatically supplements the required filter and redirects the query to scan the view.
Prerequisites
Before you begin, make sure you have:
An AnalyticDB for PostgreSQL instance
Permission to create tables and materialized views in the target database
Set up the table and materialized view
Create the
lineitemtable.CREATE TABLE lineitem ( l_orderkey bigint not null, l_partkey integer not null, l_suppkey integer not null, l_linenumber integer not null, l_quantity numeric not null, l_extendedprice numeric not null, l_discount numeric not null, l_tax numeric not null, l_returnflag "char" not null, l_linestatus "char" not null, l_shipdate date not null, l_commitdate date not null, l_receiptdate date not null, l_shipinstruct char(25) not null, l_shipmode char(10) not null, l_comment varchar(44) not null ) DISTRIBUTED BY (l_orderkey);Review the TPC-H Q1 query that you want to accelerate. The variable parameter
$1in the WHERE clause ranges from 60 to 120.SELECT l_returnflag, l_linestatus, sum(l_quantity) as sum_qty, sum(l_extendedprice) as sum_base_price, sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, avg(l_quantity) as avg_qty, avg(l_extendedprice) as avg_price, avg(l_discount) as avg_disc, count(*) as count_order FROM lineitem WHERE l_shipdate <= date '1998-12-01' - interval '$1' day -- $1 ranges from 60 to 120 GROUP BY l_returnflag, l_linestatus ORDER BY l_returnflag, l_linestatus LIMIT 1;Create the real-time materialized view
q1_mv. The view design reflects both query rewrite mechanisms:l_shipdateis added to GROUP BY to support re-aggregation when the variable WHERE filter is applied.The WHERE condition is narrowed to the lower bound of the dynamic range (
interval '60' day), so any query with an interval up to 120 days falls within the view's range.SUMandCOUNTcolumns are added for eachAVGin the original query, becauseAVGdoes not support re-aggregation directly.
CREATE INCREMENTAL MATERIALIZED VIEW q1_mv AS SELECT l_returnflag, l_linestatus, l_shipdate, sum(l_quantity) as sum_qty, sum(l_extendedprice) as sum_base_price, sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, count(*) as count_order, sum(l_extendedprice) as sum_price, sum(l_discount) as sum_disc, count(l_quantity) as count_qty, count(l_extendedprice) as count_price, count(l_discount) as count_disc FROM lineitem WHERE l_shipdate <= date '1998-12-01' - interval '60' day GROUP BY l_returnflag, l_linestatus, l_shipdate DISTRIBUTED BY (l_returnflag, l_linestatus);
Verify that query rewrite uses the materialized view
Run EXPLAIN on the original query with a specific interval value within the 60–120 range (for example, 100). The query plan should show a scan on q1_mv instead of lineitem.
EXPLAIN SELECT
l_returnflag,
l_linestatus,
sum(l_quantity) as sum_qty,
sum(l_extendedprice) as sum_base_price,
sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,
sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge,
avg(l_quantity) as avg_qty,
avg(l_extendedprice) as avg_price,
avg(l_discount) as avg_disc,
count(*) as count_order
FROM
lineitem
WHERE
l_shipdate <= date '1998-12-01' - interval '100' day
GROUP BY
l_returnflag,
l_linestatus
ORDER BY
l_returnflag,
l_linestatus
LIMIT 1;The expected query plan shows Seq Scan on q1_mv, confirming that query rewrite is active:
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Limit (cost=1.01..1.13 rows=1 width=234)
-> Gather Motion 3:1 (slice1; segments: 3) (cost=1.01..1.13 rows=1 width=234)
Merge Key: l_returnflag, l_linestatus
-> Limit (cost=1.01..1.11 rows=1 width=234)
-> GroupAggregate (cost=1.01..1.11 rows=1 width=234)
Group Key: l_returnflag, l_linestatus
-> Sort (cost=1.01..1.01 rows=1 width=194)
Sort Key: l_returnflag, l_linestatus
-> Seq Scan on q1_mv (cost=0.00..1.00 rows=1 width=194)
Filter: (l_shipdate <= '1998-08-23 00:00:00'::timestamp without time zone)
Optimizer: Postgres query optimizer
(11 rows)The Filter line confirms that query rewrite applied the variable condition (interval '100' day evaluates to 1998-08-23) as a filter on the materialized view data, rather than scanning lineitem.
Performance results
A TPC-H test on 1 TB of data, run on an AnalyticDB for PostgreSQL instance with 16 compute nodes, shows the following impact:
| Metric | Without materialized view | With real-time materialized view + query rewrite |
|---|---|---|
| Query time | 340 seconds | 0.04 seconds |
| Speed improvement | — | ~10,000x faster |