mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-15 10:11:29 +08:00
Remove 'client_min_messages = LOG' where not needed, and add the 'LOG: statement' output otherwise.
298 lines
11 KiB
SQL
298 lines
11 KiB
SQL
-- This file and its contents are licensed under the Timescale License.
|
|
-- Please see the included NOTICE for copyright information and
|
|
-- LICENSE-TIMESCALE for a copy of the license.
|
|
|
|
-- TEST SETUP --
|
|
\set ON_ERROR_STOP 0
|
|
SET client_min_messages TO NOTICE;
|
|
SET work_mem TO '64MB';
|
|
|
|
-- START OF USAGE TEST --
|
|
|
|
--First create your hypertable
|
|
CREATE TABLE device_readings (
|
|
observation_time TIMESTAMPTZ NOT NULL,
|
|
device_id TEXT NOT NULL,
|
|
metric DOUBLE PRECISION NOT NULL,
|
|
PRIMARY KEY(observation_time, device_id)
|
|
);
|
|
SELECT table_name FROM create_hypertable('device_readings', 'observation_time');
|
|
|
|
--Next, create your continuous aggregate view
|
|
CREATE MATERIALIZED VIEW device_summary
|
|
WITH (timescaledb.continuous, timescaledb.materialized_only=true) --This flag is what makes the view continuous
|
|
AS
|
|
SELECT
|
|
time_bucket('1 hour', observation_time) as bucket, --time_bucket is required
|
|
device_id,
|
|
avg(metric) as metric_avg, --We can use regular aggregates
|
|
max(metric)-min(metric) as metric_spread --We can also use expressions on aggregates and constants
|
|
FROM
|
|
device_readings
|
|
GROUP BY bucket, device_id WITH NO DATA; --We have to group by the bucket column, but can also add other group-by columns
|
|
|
|
SELECT add_continuous_aggregate_policy('device_summary', NULL, '2 h'::interval, '2 h'::interval);
|
|
--Next, insert some data into the raw hypertable
|
|
INSERT INTO device_readings
|
|
SELECT ts, 'device_1', (EXTRACT(EPOCH FROM ts)) from generate_series('2018-12-01 00:00'::timestamp, '2018-12-31 00:00'::timestamp, '30 minutes') ts;
|
|
INSERT INTO device_readings
|
|
SELECT ts, 'device_2', (EXTRACT(EPOCH FROM ts)) from generate_series('2018-12-01 00:00'::timestamp, '2018-12-31 00:00'::timestamp, '30 minutes') ts;
|
|
|
|
--Initially, it will be empty.
|
|
SELECT * FROM device_summary;
|
|
|
|
-- Simulate a policy that refreshes with lag, i.e., it doesn't refresh
|
|
-- the entire data set. In this case up to the given date.
|
|
CALL refresh_continuous_aggregate('device_summary', NULL, '2018-12-30 22:00');
|
|
|
|
--Now you can run selects over your view as normal
|
|
SELECT * FROM device_summary WHERE metric_spread = 1800 ORDER BY bucket DESC, device_id LIMIT 10;
|
|
|
|
--You can view informaton about your continuous aggregates. The meaning of these fields will be explained further down.
|
|
\x
|
|
SELECT * FROM timescaledb_information.continuous_aggregates;
|
|
|
|
\x
|
|
|
|
-- Refresh interval
|
|
--
|
|
-- The refresh interval determines how often the background worker
|
|
-- for automatic materialization will run. The default is (2 x bucket_width)
|
|
SELECT schedule_interval FROM _timescaledb_config.bgw_job WHERE id = 1000;
|
|
|
|
-- You can change this setting with ALTER VIEW (equivalently, specify in WITH clause of CREATE VIEW)
|
|
SELECT alter_job(1000, schedule_interval := '1h');
|
|
|
|
SELECT schedule_interval FROM _timescaledb_config.bgw_job WHERE id = 1000;
|
|
|
|
--
|
|
-- Refresh with lag
|
|
--
|
|
-- It is possible to use a policy or manual refresh with a lag, which
|
|
-- means the materialization will not contain the most up-to-date
|
|
-- data.
|
|
SELECT max(observation_time) FROM device_readings;
|
|
SELECT max(bucket) FROM device_summary;
|
|
CALL refresh_continuous_aggregate('device_summary', NULL, '2018-12-31 01:00');
|
|
SELECT max(observation_time) FROM device_readings;
|
|
SELECT max(bucket) FROM device_summary;
|
|
|
|
--
|
|
-- Invalidations
|
|
--
|
|
|
|
--Changes to the raw table, for values that have already been materialized are propagated asynchronously, after the materialization next runs.
|
|
--Before update:
|
|
SELECT * FROM device_summary WHERE device_id = 'device_1' and bucket = 'Sun Dec 30 13:00:00 2018 PST';
|
|
|
|
INSERT INTO device_readings VALUES ('Sun Dec 30 13:01:00 2018 PST', 'device_1', 1.0);
|
|
|
|
--Change not reflected before materializer runs.
|
|
SELECT * FROM device_summary WHERE device_id = 'device_1' and bucket = 'Sun Dec 30 13:00:00 2018 PST';
|
|
CALL refresh_continuous_aggregate('device_summary', NULL, NULL);
|
|
--But is reflected after.
|
|
SELECT * FROM device_summary WHERE device_id = 'device_1' and bucket = 'Sun Dec 30 13:00:00 2018 PST';
|
|
|
|
--
|
|
-- Dealing with timezones
|
|
--
|
|
|
|
-- You cannot use any functions that depend on the local timezone setting inside a continuous aggregate.
|
|
-- For example you cannot cast to the local time. This is because
|
|
-- a timezone setting can alter from user-to-user and thus
|
|
-- cannot be materialized.
|
|
|
|
DROP MATERIALIZED VIEW device_summary;
|
|
CREATE MATERIALIZED VIEW device_summary
|
|
WITH (timescaledb.continuous, timescaledb.materialized_only=true)
|
|
AS
|
|
SELECT
|
|
time_bucket('1 hour', observation_time) as bucket,
|
|
min(observation_time::timestamp) as min_time, --note the cast to localtime
|
|
device_id,
|
|
avg(metric) as metric_avg,
|
|
max(metric)-min(metric) as metric_spread
|
|
FROM
|
|
device_readings
|
|
GROUP BY bucket, device_id WITH NO DATA;
|
|
--note the error.
|
|
|
|
-- You have two options:
|
|
-- Option 1: be explicit in your timezone:
|
|
|
|
DROP MATERIALIZED VIEW device_summary;
|
|
CREATE MATERIALIZED VIEW device_summary
|
|
WITH (timescaledb.continuous, timescaledb.materialized_only=true)
|
|
AS
|
|
SELECT
|
|
time_bucket('1 hour', observation_time) as bucket,
|
|
min(observation_time AT TIME ZONE 'EST') as min_time, --note the explict timezone
|
|
device_id,
|
|
avg(metric) as metric_avg,
|
|
max(metric)-min(metric) as metric_spread
|
|
FROM
|
|
device_readings
|
|
GROUP BY bucket, device_id WITH NO DATA;
|
|
DROP MATERIALIZED VIEW device_summary;
|
|
|
|
-- Option 2: Keep things as TIMESTAMPTZ in the view and convert to local time when
|
|
-- querying from the view
|
|
|
|
DROP MATERIALIZED VIEW device_summary;
|
|
CREATE MATERIALIZED VIEW device_summary
|
|
WITH (timescaledb.continuous, timescaledb.materialized_only=true)
|
|
AS
|
|
SELECT
|
|
time_bucket('1 hour', observation_time) as bucket,
|
|
min(observation_time) as min_time, --this is a TIMESTAMPTZ
|
|
device_id,
|
|
avg(metric) as metric_avg,
|
|
max(metric)-min(metric) as metric_spread
|
|
FROM
|
|
device_readings
|
|
GROUP BY bucket, device_id WITH DATA;
|
|
|
|
SELECT min(min_time)::timestamp FROM device_summary;
|
|
|
|
--
|
|
-- test just in time aggregate / materialization only view
|
|
--
|
|
|
|
-- hardcoding now to 50 will lead to 30 watermark
|
|
CREATE OR REPLACE FUNCTION device_readings_int_now()
|
|
RETURNS INT LANGUAGE SQL STABLE AS
|
|
$BODY$
|
|
SELECT 50;
|
|
$BODY$;
|
|
|
|
CREATE TABLE device_readings_int(time int, value float);
|
|
SELECT create_hypertable('device_readings_int','time',chunk_time_interval:=10);
|
|
|
|
SELECT set_integer_now_func('device_readings_int','device_readings_int_now');
|
|
|
|
CREATE MATERIALIZED VIEW device_readings_mat_only
|
|
WITH (timescaledb.continuous, timescaledb.materialized_only=true)
|
|
AS
|
|
SELECT time_bucket(10,time), avg(value) FROM device_readings_int GROUP BY 1 WITH NO DATA;
|
|
|
|
CREATE MATERIALIZED VIEW device_readings_jit
|
|
WITH (timescaledb.continuous, timescaledb.materialized_only=false)
|
|
AS
|
|
SELECT time_bucket(10,time), avg(value) FROM device_readings_int GROUP BY 1 WITH NO DATA;
|
|
|
|
INSERT INTO device_readings_int SELECT i, i*10 FROM generate_series(10,40,10) AS g(i);
|
|
|
|
-- materialization only should have 0 rows
|
|
SELECT * FROM device_readings_mat_only ORDER BY time_bucket;
|
|
|
|
-- jit aggregate should have 4 rows
|
|
SELECT * FROM device_readings_jit ORDER BY time_bucket;
|
|
|
|
-- simulate a refresh policy with lag, i.e., one that doesn't refresh
|
|
-- up to the latest data. Max value is 40.
|
|
CALL refresh_continuous_aggregate('device_readings_mat_only', NULL, 30);
|
|
CALL refresh_continuous_aggregate('device_readings_jit', NULL, 30);
|
|
|
|
-- materialization only should have 2 rows
|
|
SELECT * FROM device_readings_mat_only ORDER BY time_bucket;
|
|
-- jit aggregate should have 4 rows
|
|
SELECT * FROM device_readings_jit ORDER BY time_bucket;
|
|
|
|
-- add 2 more rows
|
|
INSERT INTO device_readings_int SELECT i, i*10 FROM generate_series(50,60,10) AS g(i);
|
|
|
|
-- materialization only should have 2 rows
|
|
SELECT * FROM device_readings_mat_only ORDER BY time_bucket;
|
|
-- jit aggregate should have 6 rows
|
|
SELECT * FROM device_readings_jit ORDER BY time_bucket;
|
|
|
|
-- hardcoding now to 100 will lead to 80 watermark
|
|
CREATE OR REPLACE FUNCTION device_readings_int_now()
|
|
RETURNS INT LANGUAGE SQL STABLE AS
|
|
$BODY$
|
|
SELECT 100;
|
|
$BODY$;
|
|
|
|
-- refresh should materialize all now
|
|
CALL refresh_continuous_aggregate('device_readings_mat_only', NULL, NULL);
|
|
CALL refresh_continuous_aggregate('device_readings_jit', NULL, NULL);
|
|
|
|
-- materialization only should have 6 rows
|
|
SELECT * FROM device_readings_mat_only ORDER BY time_bucket;
|
|
-- jit aggregate should have 6 rows
|
|
SELECT * FROM device_readings_jit ORDER BY time_bucket;
|
|
|
|
-- START OF BASIC USAGE TESTS --
|
|
|
|
-- Check that continuous aggregate and materialized table is dropped
|
|
-- together.
|
|
|
|
CREATE TABLE whatever(time TIMESTAMPTZ NOT NULL, metric INTEGER);
|
|
SELECT * FROM create_hypertable('whatever', 'time');
|
|
CREATE MATERIALIZED VIEW whatever_summary WITH (timescaledb.continuous) AS
|
|
SELECT time_bucket('1 hour', time) AS bucket, avg(metric)
|
|
FROM whatever GROUP BY bucket WITH NO DATA;
|
|
|
|
SELECT (SELECT format('%1$I.%2$I', schema_name, table_name)::regclass::oid
|
|
FROM _timescaledb_catalog.hypertable
|
|
WHERE id = raw_hypertable_id) AS raw_table
|
|
, (SELECT format('%1$I.%2$I', schema_name, table_name)::regclass::oid
|
|
FROM _timescaledb_catalog.hypertable
|
|
WHERE id = mat_hypertable_id) AS mat_table
|
|
FROM _timescaledb_catalog.continuous_agg
|
|
WHERE user_view_name = 'whatever_summary' \gset
|
|
SELECT relname FROM pg_class WHERE oid = :mat_table;
|
|
|
|
----------------------------------------------------------------
|
|
-- Should generate an error since the cagg is dependent on the table.
|
|
DROP TABLE whatever;
|
|
|
|
----------------------------------------------------------------
|
|
-- Checking that a cagg cannot be dropped if there is a dependent
|
|
-- object on it.
|
|
CREATE VIEW whatever_summary_dependency AS SELECT * FROM whatever_summary;
|
|
|
|
-- Should generate an error
|
|
DROP MATERIALIZED VIEW whatever_summary;
|
|
|
|
-- Dropping the dependent view so that we can do a proper drop below.
|
|
DROP VIEW whatever_summary_dependency;
|
|
|
|
----------------------------------------------------------------
|
|
-- Dropping the cagg should also remove the materialized table
|
|
DROP MATERIALIZED VIEW whatever_summary;
|
|
SELECT relname FROM pg_class WHERE oid = :mat_table;
|
|
|
|
----------------------------------------------------------------
|
|
-- Cleanup
|
|
DROP TABLE whatever;
|
|
|
|
-- END OF BASIC USAGE TESTS --
|
|
|
|
CREATE TABLE metrics(time timestamptz, device TEXT, value float);
|
|
SELECT table_name FROM create_hypertable('metrics','time');
|
|
INSERT INTO metrics SELECT generate_series('1999-12-20'::timestamptz,'2000-02-01'::timestamptz,'12 day'::interval), 'dev1', 0.25;
|
|
|
|
SELECT current_setting('timezone');
|
|
|
|
-- should be blocked because non-immutable expression
|
|
\set ON_ERROR_STOP 0
|
|
CREATE MATERIALIZED VIEW cagg1 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 day', time, current_setting('timezone')) FROM metrics GROUP BY 1;
|
|
\set ON_ERROR_STOP 1
|
|
|
|
CREATE MATERIALIZED VIEW cagg1 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 day', time, 'PST8PDT') FROM metrics GROUP BY 1;
|
|
SELECT * FROM cagg1;
|
|
|
|
CREATE MATERIALIZED VIEW cagg2 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 month', time, 'PST8PDT') FROM metrics GROUP BY 1;
|
|
SELECT * FROM cagg2;
|
|
|
|
-- custom origin
|
|
CREATE MATERIALIZED VIEW cagg3 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 month', time, 'PST8PDT', '2000-01-01'::timestamptz) FROM metrics GROUP BY 1;
|
|
SELECT * FROM cagg3;
|
|
|
|
-- offset not supported atm
|
|
\set ON_ERROR_STOP 0
|
|
CREATE MATERIALIZED VIEW cagg4 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 month', time, 'PST8PDT', "offset":= INTERVAL '15 day') FROM metrics GROUP BY 1;
|
|
\set ON_ERROR_STOP 1
|
|
|