mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-31 01:15:18 +08:00
This patch optimizes how first()/last() initialize the compare function. Previously the compare function would be looked up for every transition function call but since polymorphic types are resolved at parse time for a specific aggregate instance the compare function will not change during its lifetime. Additionally this patch also fixes a memory leak when using first()/last() on pointer types. These changes lead to a roughly 2x speed improvement for first()/ last() and make the memory usage near-constant. Fixes #3935
1186 lines
56 KiB
Plaintext
1186 lines
56 KiB
Plaintext
-- This file and its contents are licensed under the Apache License 2.0.
|
|
-- Please see the included NOTICE for copyright information and
|
|
-- LICENSE-APACHE for a copy of the license.
|
|
\set TEST_BASE_NAME agg_bookends
|
|
SELECT format('include/%s_load.sql', :'TEST_BASE_NAME') as "TEST_LOAD_NAME",
|
|
format('include/%s_query.sql', :'TEST_BASE_NAME') as "TEST_QUERY_NAME",
|
|
format('%s/results/%s_results_optimized.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_OPTIMIZED",
|
|
format('%s/results/%s_results_unoptimized.out', :'TEST_OUTPUT_DIR', :'TEST_BASE_NAME') as "TEST_RESULTS_UNOPTIMIZED"
|
|
\gset
|
|
SELECT format('\! diff -u --label "Unoptimized result" --label "Optimized result" %s %s', :'TEST_RESULTS_UNOPTIMIZED', :'TEST_RESULTS_OPTIMIZED') as "DIFF_CMD"
|
|
\gset
|
|
\set PREFIX 'EXPLAIN (analyze, costs off, timing off, summary off)'
|
|
\ir :TEST_LOAD_NAME
|
|
-- This file and its contents are licensed under the Apache License 2.0.
|
|
-- Please see the included NOTICE for copyright information and
|
|
-- LICENSE-APACHE for a copy of the license.
|
|
CREATE TABLE btest(time timestamp NOT NULL, time_alt timestamp, gp INTEGER, temp float, strid TEXT DEFAULT 'testing');
|
|
SELECT schema_name, table_name, created FROM create_hypertable('btest', 'time');
|
|
schema_name | table_name | created
|
|
-------------+------------+---------
|
|
public | btest | t
|
|
(1 row)
|
|
|
|
INSERT INTO btest VALUES('2017-01-20T09:00:01', '2017-01-20T10:00:00', 1, 22.5);
|
|
INSERT INTO btest VALUES('2017-01-20T09:00:21', '2017-01-20T09:00:59', 1, 21.2);
|
|
INSERT INTO btest VALUES('2017-01-20T09:00:47', '2017-01-20T09:00:58', 1, 25.1);
|
|
INSERT INTO btest VALUES('2017-01-20T09:00:02', '2017-01-20T09:00:57', 2, 35.5);
|
|
INSERT INTO btest VALUES('2017-01-20T09:00:21', '2017-01-20T09:00:56', 2, 30.2);
|
|
--TOASTED;
|
|
INSERT INTO btest VALUES('2017-01-20T09:00:43', '2017-01-20T09:01:55', 2, 20.1, repeat('xyz', 1000000) );
|
|
CREATE TABLE btest_numeric (time timestamp NOT NULL, quantity numeric);
|
|
SELECT schema_name, table_name, created FROM create_hypertable('btest_numeric', 'time');
|
|
schema_name | table_name | created
|
|
-------------+---------------+---------
|
|
public | btest_numeric | t
|
|
(1 row)
|
|
|
|
\ir :TEST_QUERY_NAME
|
|
-- This file and its contents are licensed under the Apache License 2.0.
|
|
-- Please see the included NOTICE for copyright information and
|
|
-- LICENSE-APACHE for a copy of the license.
|
|
-- canary for results diff
|
|
-- this should be only output of results diff
|
|
SELECT setting, current_setting(setting) AS value from (VALUES ('timescaledb.enable_optimizations')) v(setting);
|
|
setting | value
|
|
----------------------------------+-------
|
|
timescaledb.enable_optimizations | on
|
|
(1 row)
|
|
|
|
:PREFIX SELECT time, gp, temp FROM btest ORDER BY time;
|
|
QUERY PLAN
|
|
-------------------------------------------------------------------------------------------------------------
|
|
Custom Scan (ChunkAppend) on btest (actual rows=6 loops=1)
|
|
Order: btest."time"
|
|
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
(3 rows)
|
|
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(7 rows)
|
|
|
|
:PREFIX SELECT first(temp, time) FROM btest;
|
|
QUERY PLAN
|
|
---------------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time"
|
|
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(7 rows)
|
|
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------
|
|
Aggregate (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
(2 rows)
|
|
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------
|
|
Aggregate (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
(2 rows)
|
|
|
|
:PREFIX SELECT gp, last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Sort (actual rows=2 loops=1)
|
|
Sort Key: _hyper_1_1_chunk.gp
|
|
Sort Method: quicksort
|
|
-> HashAggregate (actual rows=2 loops=1)
|
|
Group Key: _hyper_1_1_chunk.gp
|
|
Batches: 1
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
(7 rows)
|
|
|
|
:PREFIX SELECT gp, first(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Sort (actual rows=2 loops=1)
|
|
Sort Key: _hyper_1_1_chunk.gp
|
|
Sort Method: quicksort
|
|
-> HashAggregate (actual rows=2 loops=1)
|
|
Group Key: _hyper_1_1_chunk.gp
|
|
Batches: 1
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
(7 rows)
|
|
|
|
--check whole row
|
|
:PREFIX SELECT gp, first(btest, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Sort (actual rows=2 loops=1)
|
|
Sort Key: _hyper_1_1_chunk.gp
|
|
Sort Method: quicksort
|
|
-> HashAggregate (actual rows=2 loops=1)
|
|
Group Key: _hyper_1_1_chunk.gp
|
|
Batches: 1
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
(7 rows)
|
|
|
|
--check toasted col
|
|
:PREFIX SELECT gp, left(last(strid, time), 10) FROM btest GROUP BY gp ORDER BY gp;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Sort (actual rows=2 loops=1)
|
|
Sort Key: _hyper_1_1_chunk.gp
|
|
Sort Method: quicksort
|
|
-> HashAggregate (actual rows=2 loops=1)
|
|
Group Key: _hyper_1_1_chunk.gp
|
|
Batches: 1
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
(7 rows)
|
|
|
|
:PREFIX SELECT gp, last(temp, strid) FROM btest GROUP BY gp ORDER BY gp;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Sort (actual rows=2 loops=1)
|
|
Sort Key: _hyper_1_1_chunk.gp
|
|
Sort Method: quicksort
|
|
-> HashAggregate (actual rows=2 loops=1)
|
|
Group Key: _hyper_1_1_chunk.gp
|
|
Batches: 1
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
(7 rows)
|
|
|
|
:PREFIX SELECT gp, last(strid, temp) FROM btest GROUP BY gp ORDER BY gp;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Sort (actual rows=2 loops=1)
|
|
Sort Key: _hyper_1_1_chunk.gp
|
|
Sort Method: quicksort
|
|
-> HashAggregate (actual rows=2 loops=1)
|
|
Group Key: _hyper_1_1_chunk.gp
|
|
Batches: 1
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
(7 rows)
|
|
|
|
BEGIN;
|
|
--check null value as last element
|
|
INSERT INTO btest VALUES('2018-01-20T09:00:43', '2017-01-20T09:00:55', 2, NULL);
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(9 rows)
|
|
|
|
--check non-null element "overrides" NULL because it comes after.
|
|
INSERT INTO btest VALUES('2019-01-20T09:00:43', '2018-01-20T09:00:55', 2, 30.5);
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(11 rows)
|
|
|
|
--check null cmp element is skipped
|
|
INSERT INTO btest VALUES('2018-01-20T09:00:43', NULL, 2, 32.3);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Aggregate (actual rows=1 loops=1)
|
|
-> Append (actual rows=9 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
(5 rows)
|
|
|
|
-- fist returns NULL value
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Aggregate (actual rows=1 loops=1)
|
|
-> Append (actual rows=9 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
(5 rows)
|
|
|
|
-- test first return non NULL value
|
|
INSERT INTO btest VALUES('2016-01-20T09:00:00', '2016-01-20T09:00:00', 2, 36.5);
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Aggregate (actual rows=1 loops=1)
|
|
-> Append (actual rows=10 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
(6 rows)
|
|
|
|
--check non null cmp element insert after null cmp
|
|
INSERT INTO btest VALUES('2020-01-20T09:00:43', '2020-01-20T09:00:43', 2, 35.3);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Aggregate (actual rows=1 loops=1)
|
|
-> Append (actual rows=11 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
(7 rows)
|
|
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Aggregate (actual rows=1 loops=1)
|
|
-> Append (actual rows=11 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
(7 rows)
|
|
|
|
--cmp nulls should be ignored and not present in groups
|
|
:PREFIX SELECT gp, last(temp, time_alt) FROM btest GROUP BY gp ORDER BY gp;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------
|
|
Sort (actual rows=2 loops=1)
|
|
Sort Key: _hyper_1_1_chunk.gp
|
|
Sort Method: quicksort
|
|
-> HashAggregate (actual rows=2 loops=1)
|
|
Group Key: _hyper_1_1_chunk.gp
|
|
Batches: 1
|
|
-> Append (actual rows=11 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
(12 rows)
|
|
|
|
--Previously, some bugs were found with NULLS and numeric types, so test that
|
|
INSERT INTO btest_numeric VALUES ('2019-01-20T09:00:43', NULL);
|
|
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
|
|
QUERY PLAN
|
|
--------------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest_numeric (actual rows=1 loops=1)
|
|
Order: btest_numeric."time" DESC
|
|
-> Index Scan using _hyper_2_6_chunk_btest_numeric_time_idx on _hyper_2_6_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(7 rows)
|
|
|
|
--check non-null element "overrides" NULL because it comes after.
|
|
INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5);
|
|
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
|
|
QUERY PLAN
|
|
--------------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest_numeric (actual rows=1 loops=1)
|
|
Order: btest_numeric."time" DESC
|
|
-> Index Scan using _hyper_2_7_chunk_btest_numeric_time_idx on _hyper_2_7_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_2_6_chunk_btest_numeric_time_idx on _hyper_2_6_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(9 rows)
|
|
|
|
-- do index scan for last
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(15 rows)
|
|
|
|
-- do index scan for first
|
|
:PREFIX SELECT first(temp, time) FROM btest;
|
|
QUERY PLAN
|
|
---------------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time"
|
|
-> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(15 rows)
|
|
|
|
-- can't do index scan when ordering on non-index column
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Aggregate (actual rows=1 loops=1)
|
|
-> Append (actual rows=11 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
(7 rows)
|
|
|
|
-- do index scan for subquery
|
|
:PREFIX SELECT * FROM (SELECT last(temp, time) FROM btest) last;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(15 rows)
|
|
|
|
-- can't do index scan when using group by
|
|
:PREFIX SELECT last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------
|
|
Sort (actual rows=2 loops=1)
|
|
Sort Key: _hyper_1_1_chunk.gp
|
|
Sort Method: quicksort
|
|
-> HashAggregate (actual rows=2 loops=1)
|
|
Group Key: _hyper_1_1_chunk.gp
|
|
Batches: 1
|
|
-> Append (actual rows=11 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
(12 rows)
|
|
|
|
-- do index scan when agg function is used in CTE subquery
|
|
:PREFIX WITH last_temp AS (SELECT last(temp, time) FROM btest) SELECT * from last_temp;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(15 rows)
|
|
|
|
-- do index scan when using both FIRST and LAST aggregate functions
|
|
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
|
|
QUERY PLAN
|
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $1)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
InitPlan 2 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1 loops=1)
|
|
Order: btest_1."time"
|
|
-> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(29 rows)
|
|
|
|
-- verify results when using both FIRST and LAST
|
|
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
|
|
QUERY PLAN
|
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $1)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
InitPlan 2 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest btest_1 (actual rows=1 loops=1)
|
|
Order: btest_1."time"
|
|
-> Index Scan Backward using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk _hyper_1_4_chunk_1 (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk _hyper_1_1_chunk_1 (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk _hyper_1_2_chunk_1 (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk _hyper_1_3_chunk_1 (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk _hyper_1_5_chunk_1 (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(29 rows)
|
|
|
|
-- do index scan when using WHERE
|
|
:PREFIX SELECT last(temp, time) FROM btest WHERE time <= '2017-01-20T09:00:02';
|
|
QUERY PLAN
|
|
----------------------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
|
|
Index Cond: (("time" IS NOT NULL) AND ("time" <= 'Fri Jan 20 09:00:02 2017'::timestamp without time zone))
|
|
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
|
|
Index Cond: (("time" IS NOT NULL) AND ("time" <= 'Fri Jan 20 09:00:02 2017'::timestamp without time zone))
|
|
(9 rows)
|
|
|
|
-- can't do index scan for MAX and LAST combined (MinMax optimization fails when having different aggregate functions)
|
|
:PREFIX SELECT max(time), last(temp, time) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------
|
|
Aggregate (actual rows=1 loops=1)
|
|
-> Append (actual rows=11 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
(7 rows)
|
|
|
|
-- can't do index scan when using FIRST/LAST in ORDER BY
|
|
:PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time);
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------
|
|
Sort (actual rows=1 loops=1)
|
|
Sort Key: (last(_hyper_1_1_chunk.temp, _hyper_1_1_chunk."time"))
|
|
Sort Method: quicksort
|
|
-> Aggregate (actual rows=1 loops=1)
|
|
-> Append (actual rows=11 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
(10 rows)
|
|
|
|
-- do index scan
|
|
:PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=0 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
Filter: (temp < '30'::double precision)
|
|
Rows Removed by Filter: 1
|
|
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (actual rows=0 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
Filter: (temp < '30'::double precision)
|
|
Rows Removed by Filter: 1
|
|
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (actual rows=0 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
Filter: (temp < '30'::double precision)
|
|
Rows Removed by Filter: 2
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
Filter: (temp < '30'::double precision)
|
|
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
Filter: (temp < '30'::double precision)
|
|
(23 rows)
|
|
|
|
-- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
|
|
-- do index scan
|
|
:PREFIX SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
|
|
QUERY PLAN
|
|
----------------------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time"
|
|
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
|
|
Index Cond: (("time" IS NOT NULL) AND ("time" >= 'Fri Jan 20 09:00:47 2017'::timestamp without time zone))
|
|
-> Index Scan Backward using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
|
|
Index Cond: (("time" IS NOT NULL) AND ("time" >= 'Fri Jan 20 09:00:47 2017'::timestamp without time zone))
|
|
-> Index Scan Backward using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
|
|
Index Cond: (("time" IS NOT NULL) AND ("time" >= 'Fri Jan 20 09:00:47 2017'::timestamp without time zone))
|
|
-> Index Scan Backward using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (never executed)
|
|
Index Cond: (("time" IS NOT NULL) AND ("time" >= 'Fri Jan 20 09:00:47 2017'::timestamp without time zone))
|
|
(13 rows)
|
|
|
|
-- can't do index scan when using WINDOW function
|
|
:PREFIX SELECT gp, last(temp, time) OVER (PARTITION BY gp) AS last FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------
|
|
WindowAgg (actual rows=11 loops=1)
|
|
-> Sort (actual rows=11 loops=1)
|
|
Sort Key: _hyper_1_1_chunk.gp
|
|
Sort Method: quicksort
|
|
-> Append (actual rows=11 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
(10 rows)
|
|
|
|
-- test constants
|
|
:PREFIX SELECT first(100, 100) FROM btest;
|
|
QUERY PLAN
|
|
--------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Result (actual rows=1 loops=1)
|
|
-> Append (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (never executed)
|
|
-> Seq Scan on _hyper_1_3_chunk (never executed)
|
|
-> Seq Scan on _hyper_1_4_chunk (never executed)
|
|
-> Seq Scan on _hyper_1_5_chunk (never executed)
|
|
(10 rows)
|
|
|
|
-- create an index so we can test optimization
|
|
CREATE INDEX btest_time_alt_idx ON btest(time_alt);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
QUERY PLAN
|
|
-------------------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Merge Append (actual rows=1 loops=1)
|
|
Sort Key: _hyper_1_1_chunk.time_alt DESC
|
|
-> Index Scan Backward using _hyper_1_1_chunk_btest_time_alt_idx on _hyper_1_1_chunk (actual rows=1 loops=1)
|
|
Index Cond: (time_alt IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_2_chunk_btest_time_alt_idx on _hyper_1_2_chunk (actual rows=1 loops=1)
|
|
Index Cond: (time_alt IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_3_chunk_btest_time_alt_idx on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
Index Cond: (time_alt IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_4_chunk_btest_time_alt_idx on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
Index Cond: (time_alt IS NOT NULL)
|
|
-> Index Scan Backward using _hyper_1_5_chunk_btest_time_alt_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
Index Cond: (time_alt IS NOT NULL)
|
|
(15 rows)
|
|
|
|
--test nested FIRST/LAST - should optimize
|
|
:PREFIX SELECT abs(last(temp, time)) FROM btest;
|
|
QUERY PLAN
|
|
------------------------------------------------------------------------------------------------------------------
|
|
Result (actual rows=1 loops=1)
|
|
InitPlan 1 (returns $0)
|
|
-> Limit (actual rows=1 loops=1)
|
|
-> Custom Scan (ChunkAppend) on btest (actual rows=1 loops=1)
|
|
Order: btest."time" DESC
|
|
-> Index Scan using _hyper_1_5_chunk_btest_time_idx on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_3_chunk_btest_time_idx on _hyper_1_3_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_2_chunk_btest_time_idx on _hyper_1_2_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_1_chunk_btest_time_idx on _hyper_1_1_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
-> Index Scan using _hyper_1_4_chunk_btest_time_idx on _hyper_1_4_chunk (never executed)
|
|
Index Cond: ("time" IS NOT NULL)
|
|
(15 rows)
|
|
|
|
-- test nested FIRST/LAST in ORDER BY - no optimization possible
|
|
:PREFIX SELECT abs(last(temp, time)) FROM btest ORDER BY abs(last(temp,time));
|
|
QUERY PLAN
|
|
-------------------------------------------------------------------------
|
|
Sort (actual rows=1 loops=1)
|
|
Sort Key: (abs(last(_hyper_1_1_chunk.temp, _hyper_1_1_chunk."time")))
|
|
Sort Method: quicksort
|
|
-> Aggregate (actual rows=1 loops=1)
|
|
-> Append (actual rows=11 loops=1)
|
|
-> Seq Scan on _hyper_1_1_chunk (actual rows=6 loops=1)
|
|
-> Seq Scan on _hyper_1_2_chunk (actual rows=2 loops=1)
|
|
-> Seq Scan on _hyper_1_3_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_4_chunk (actual rows=1 loops=1)
|
|
-> Seq Scan on _hyper_1_5_chunk (actual rows=1 loops=1)
|
|
(10 rows)
|
|
|
|
ROLLBACK;
|
|
-- we want test results as part of the output too to make sure we produce correct output
|
|
\set PREFIX ''
|
|
\ir :TEST_QUERY_NAME
|
|
-- This file and its contents are licensed under the Apache License 2.0.
|
|
-- Please see the included NOTICE for copyright information and
|
|
-- LICENSE-APACHE for a copy of the license.
|
|
-- canary for results diff
|
|
-- this should be only output of results diff
|
|
SELECT setting, current_setting(setting) AS value from (VALUES ('timescaledb.enable_optimizations')) v(setting);
|
|
setting | value
|
|
----------------------------------+-------
|
|
timescaledb.enable_optimizations | on
|
|
(1 row)
|
|
|
|
:PREFIX SELECT time, gp, temp FROM btest ORDER BY time;
|
|
time | gp | temp
|
|
--------------------------+----+------
|
|
Fri Jan 20 09:00:01 2017 | 1 | 22.5
|
|
Fri Jan 20 09:00:02 2017 | 2 | 35.5
|
|
Fri Jan 20 09:00:21 2017 | 1 | 21.2
|
|
Fri Jan 20 09:00:21 2017 | 2 | 30.2
|
|
Fri Jan 20 09:00:43 2017 | 2 | 20.1
|
|
Fri Jan 20 09:00:47 2017 | 1 | 25.1
|
|
(6 rows)
|
|
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
last
|
|
------
|
|
25.1
|
|
(1 row)
|
|
|
|
:PREFIX SELECT first(temp, time) FROM btest;
|
|
first
|
|
-------
|
|
22.5
|
|
(1 row)
|
|
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
last
|
|
------
|
|
22.5
|
|
(1 row)
|
|
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
first
|
|
-------
|
|
30.2
|
|
(1 row)
|
|
|
|
:PREFIX SELECT gp, last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
gp | last
|
|
----+------
|
|
1 | 25.1
|
|
2 | 20.1
|
|
(2 rows)
|
|
|
|
:PREFIX SELECT gp, first(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
gp | first
|
|
----+-------
|
|
1 | 22.5
|
|
2 | 35.5
|
|
(2 rows)
|
|
|
|
--check whole row
|
|
:PREFIX SELECT gp, first(btest, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
gp | first
|
|
----+------------------------------------------------------------------------
|
|
1 | ("Fri Jan 20 09:00:01 2017","Fri Jan 20 10:00:00 2017",1,22.5,testing)
|
|
2 | ("Fri Jan 20 09:00:02 2017","Fri Jan 20 09:00:57 2017",2,35.5,testing)
|
|
(2 rows)
|
|
|
|
--check toasted col
|
|
:PREFIX SELECT gp, left(last(strid, time), 10) FROM btest GROUP BY gp ORDER BY gp;
|
|
gp | left
|
|
----+------------
|
|
1 | testing
|
|
2 | xyzxyzxyzx
|
|
(2 rows)
|
|
|
|
:PREFIX SELECT gp, last(temp, strid) FROM btest GROUP BY gp ORDER BY gp;
|
|
gp | last
|
|
----+------
|
|
1 | 22.5
|
|
2 | 20.1
|
|
(2 rows)
|
|
|
|
:PREFIX SELECT gp, last(strid, temp) FROM btest GROUP BY gp ORDER BY gp;
|
|
gp | last
|
|
----+---------
|
|
1 | testing
|
|
2 | testing
|
|
(2 rows)
|
|
|
|
BEGIN;
|
|
--check null value as last element
|
|
INSERT INTO btest VALUES('2018-01-20T09:00:43', '2017-01-20T09:00:55', 2, NULL);
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
last
|
|
------
|
|
|
|
(1 row)
|
|
|
|
--check non-null element "overrides" NULL because it comes after.
|
|
INSERT INTO btest VALUES('2019-01-20T09:00:43', '2018-01-20T09:00:55', 2, 30.5);
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
last
|
|
------
|
|
30.5
|
|
(1 row)
|
|
|
|
--check null cmp element is skipped
|
|
INSERT INTO btest VALUES('2018-01-20T09:00:43', NULL, 2, 32.3);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
last
|
|
------
|
|
30.5
|
|
(1 row)
|
|
|
|
-- fist returns NULL value
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
first
|
|
-------
|
|
|
|
(1 row)
|
|
|
|
-- test first return non NULL value
|
|
INSERT INTO btest VALUES('2016-01-20T09:00:00', '2016-01-20T09:00:00', 2, 36.5);
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
first
|
|
-------
|
|
36.5
|
|
(1 row)
|
|
|
|
--check non null cmp element insert after null cmp
|
|
INSERT INTO btest VALUES('2020-01-20T09:00:43', '2020-01-20T09:00:43', 2, 35.3);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
last
|
|
------
|
|
35.3
|
|
(1 row)
|
|
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
first
|
|
-------
|
|
36.5
|
|
(1 row)
|
|
|
|
--cmp nulls should be ignored and not present in groups
|
|
:PREFIX SELECT gp, last(temp, time_alt) FROM btest GROUP BY gp ORDER BY gp;
|
|
gp | last
|
|
----+------
|
|
1 | 22.5
|
|
2 | 35.3
|
|
(2 rows)
|
|
|
|
--Previously, some bugs were found with NULLS and numeric types, so test that
|
|
INSERT INTO btest_numeric VALUES ('2019-01-20T09:00:43', NULL);
|
|
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
|
|
last
|
|
------
|
|
|
|
(1 row)
|
|
|
|
--check non-null element "overrides" NULL because it comes after.
|
|
INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5);
|
|
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
|
|
last
|
|
------
|
|
30.5
|
|
(1 row)
|
|
|
|
-- do index scan for last
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
last
|
|
------
|
|
35.3
|
|
(1 row)
|
|
|
|
-- do index scan for first
|
|
:PREFIX SELECT first(temp, time) FROM btest;
|
|
first
|
|
-------
|
|
36.5
|
|
(1 row)
|
|
|
|
-- can't do index scan when ordering on non-index column
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
first
|
|
-------
|
|
36.5
|
|
(1 row)
|
|
|
|
-- do index scan for subquery
|
|
:PREFIX SELECT * FROM (SELECT last(temp, time) FROM btest) last;
|
|
last
|
|
------
|
|
35.3
|
|
(1 row)
|
|
|
|
-- can't do index scan when using group by
|
|
:PREFIX SELECT last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
last
|
|
------
|
|
25.1
|
|
35.3
|
|
(2 rows)
|
|
|
|
-- do index scan when agg function is used in CTE subquery
|
|
:PREFIX WITH last_temp AS (SELECT last(temp, time) FROM btest) SELECT * from last_temp;
|
|
last
|
|
------
|
|
35.3
|
|
(1 row)
|
|
|
|
-- do index scan when using both FIRST and LAST aggregate functions
|
|
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
|
|
first | last
|
|
-------+------
|
|
36.5 | 35.3
|
|
(1 row)
|
|
|
|
-- verify results when using both FIRST and LAST
|
|
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
|
|
first | last
|
|
-------+------
|
|
36.5 | 35.3
|
|
(1 row)
|
|
|
|
-- do index scan when using WHERE
|
|
:PREFIX SELECT last(temp, time) FROM btest WHERE time <= '2017-01-20T09:00:02';
|
|
last
|
|
------
|
|
35.5
|
|
(1 row)
|
|
|
|
-- can't do index scan for MAX and LAST combined (MinMax optimization fails when having different aggregate functions)
|
|
:PREFIX SELECT max(time), last(temp, time) FROM btest;
|
|
max | last
|
|
--------------------------+------
|
|
Mon Jan 20 09:00:43 2020 | 35.3
|
|
(1 row)
|
|
|
|
-- can't do index scan when using FIRST/LAST in ORDER BY
|
|
:PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time);
|
|
last
|
|
------
|
|
35.3
|
|
(1 row)
|
|
|
|
-- do index scan
|
|
:PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30;
|
|
last
|
|
------
|
|
25.1
|
|
(1 row)
|
|
|
|
-- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
|
|
-- do index scan
|
|
:PREFIX SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
|
|
first
|
|
-------
|
|
25.1
|
|
(1 row)
|
|
|
|
-- can't do index scan when using WINDOW function
|
|
:PREFIX SELECT gp, last(temp, time) OVER (PARTITION BY gp) AS last FROM btest;
|
|
gp | last
|
|
----+------
|
|
1 | 25.1
|
|
1 | 25.1
|
|
1 | 25.1
|
|
2 | 35.3
|
|
2 | 35.3
|
|
2 | 35.3
|
|
2 | 35.3
|
|
2 | 35.3
|
|
2 | 35.3
|
|
2 | 35.3
|
|
2 | 35.3
|
|
(11 rows)
|
|
|
|
-- test constants
|
|
:PREFIX SELECT first(100, 100) FROM btest;
|
|
first
|
|
-------
|
|
100
|
|
(1 row)
|
|
|
|
-- create an index so we can test optimization
|
|
CREATE INDEX btest_time_alt_idx ON btest(time_alt);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
last
|
|
------
|
|
35.3
|
|
(1 row)
|
|
|
|
--test nested FIRST/LAST - should optimize
|
|
:PREFIX SELECT abs(last(temp, time)) FROM btest;
|
|
abs
|
|
------
|
|
35.3
|
|
(1 row)
|
|
|
|
-- test nested FIRST/LAST in ORDER BY - no optimization possible
|
|
:PREFIX SELECT abs(last(temp, time)) FROM btest ORDER BY abs(last(temp,time));
|
|
abs
|
|
------
|
|
35.3
|
|
(1 row)
|
|
|
|
ROLLBACK;
|
|
-- diff results with optimizations disabled and enabled
|
|
\o :TEST_RESULTS_UNOPTIMIZED
|
|
SET timescaledb.enable_optimizations TO false;
|
|
\ir :TEST_QUERY_NAME
|
|
-- This file and its contents are licensed under the Apache License 2.0.
|
|
-- Please see the included NOTICE for copyright information and
|
|
-- LICENSE-APACHE for a copy of the license.
|
|
-- canary for results diff
|
|
-- this should be only output of results diff
|
|
SELECT setting, current_setting(setting) AS value from (VALUES ('timescaledb.enable_optimizations')) v(setting);
|
|
:PREFIX SELECT time, gp, temp FROM btest ORDER BY time;
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
:PREFIX SELECT first(temp, time) FROM btest;
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
:PREFIX SELECT gp, last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
:PREFIX SELECT gp, first(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
--check whole row
|
|
:PREFIX SELECT gp, first(btest, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
--check toasted col
|
|
:PREFIX SELECT gp, left(last(strid, time), 10) FROM btest GROUP BY gp ORDER BY gp;
|
|
:PREFIX SELECT gp, last(temp, strid) FROM btest GROUP BY gp ORDER BY gp;
|
|
:PREFIX SELECT gp, last(strid, temp) FROM btest GROUP BY gp ORDER BY gp;
|
|
BEGIN;
|
|
--check null value as last element
|
|
INSERT INTO btest VALUES('2018-01-20T09:00:43', '2017-01-20T09:00:55', 2, NULL);
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
--check non-null element "overrides" NULL because it comes after.
|
|
INSERT INTO btest VALUES('2019-01-20T09:00:43', '2018-01-20T09:00:55', 2, 30.5);
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
--check null cmp element is skipped
|
|
INSERT INTO btest VALUES('2018-01-20T09:00:43', NULL, 2, 32.3);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
-- fist returns NULL value
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
-- test first return non NULL value
|
|
INSERT INTO btest VALUES('2016-01-20T09:00:00', '2016-01-20T09:00:00', 2, 36.5);
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
--check non null cmp element insert after null cmp
|
|
INSERT INTO btest VALUES('2020-01-20T09:00:43', '2020-01-20T09:00:43', 2, 35.3);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
--cmp nulls should be ignored and not present in groups
|
|
:PREFIX SELECT gp, last(temp, time_alt) FROM btest GROUP BY gp ORDER BY gp;
|
|
--Previously, some bugs were found with NULLS and numeric types, so test that
|
|
INSERT INTO btest_numeric VALUES ('2019-01-20T09:00:43', NULL);
|
|
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
|
|
--check non-null element "overrides" NULL because it comes after.
|
|
INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5);
|
|
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
|
|
-- do index scan for last
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
-- do index scan for first
|
|
:PREFIX SELECT first(temp, time) FROM btest;
|
|
-- can't do index scan when ordering on non-index column
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
-- do index scan for subquery
|
|
:PREFIX SELECT * FROM (SELECT last(temp, time) FROM btest) last;
|
|
-- can't do index scan when using group by
|
|
:PREFIX SELECT last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
-- do index scan when agg function is used in CTE subquery
|
|
:PREFIX WITH last_temp AS (SELECT last(temp, time) FROM btest) SELECT * from last_temp;
|
|
-- do index scan when using both FIRST and LAST aggregate functions
|
|
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
|
|
-- verify results when using both FIRST and LAST
|
|
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
|
|
-- do index scan when using WHERE
|
|
:PREFIX SELECT last(temp, time) FROM btest WHERE time <= '2017-01-20T09:00:02';
|
|
-- can't do index scan for MAX and LAST combined (MinMax optimization fails when having different aggregate functions)
|
|
:PREFIX SELECT max(time), last(temp, time) FROM btest;
|
|
-- can't do index scan when using FIRST/LAST in ORDER BY
|
|
:PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time);
|
|
-- do index scan
|
|
:PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30;
|
|
-- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
|
|
-- do index scan
|
|
:PREFIX SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
|
|
-- can't do index scan when using WINDOW function
|
|
:PREFIX SELECT gp, last(temp, time) OVER (PARTITION BY gp) AS last FROM btest;
|
|
-- test constants
|
|
:PREFIX SELECT first(100, 100) FROM btest;
|
|
-- create an index so we can test optimization
|
|
CREATE INDEX btest_time_alt_idx ON btest(time_alt);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
--test nested FIRST/LAST - should optimize
|
|
:PREFIX SELECT abs(last(temp, time)) FROM btest;
|
|
-- test nested FIRST/LAST in ORDER BY - no optimization possible
|
|
:PREFIX SELECT abs(last(temp, time)) FROM btest ORDER BY abs(last(temp,time));
|
|
ROLLBACK;
|
|
\o
|
|
\o :TEST_RESULTS_OPTIMIZED
|
|
SET timescaledb.enable_optimizations TO true;
|
|
\ir :TEST_QUERY_NAME
|
|
-- This file and its contents are licensed under the Apache License 2.0.
|
|
-- Please see the included NOTICE for copyright information and
|
|
-- LICENSE-APACHE for a copy of the license.
|
|
-- canary for results diff
|
|
-- this should be only output of results diff
|
|
SELECT setting, current_setting(setting) AS value from (VALUES ('timescaledb.enable_optimizations')) v(setting);
|
|
:PREFIX SELECT time, gp, temp FROM btest ORDER BY time;
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
:PREFIX SELECT first(temp, time) FROM btest;
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
:PREFIX SELECT gp, last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
:PREFIX SELECT gp, first(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
--check whole row
|
|
:PREFIX SELECT gp, first(btest, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
--check toasted col
|
|
:PREFIX SELECT gp, left(last(strid, time), 10) FROM btest GROUP BY gp ORDER BY gp;
|
|
:PREFIX SELECT gp, last(temp, strid) FROM btest GROUP BY gp ORDER BY gp;
|
|
:PREFIX SELECT gp, last(strid, temp) FROM btest GROUP BY gp ORDER BY gp;
|
|
BEGIN;
|
|
--check null value as last element
|
|
INSERT INTO btest VALUES('2018-01-20T09:00:43', '2017-01-20T09:00:55', 2, NULL);
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
--check non-null element "overrides" NULL because it comes after.
|
|
INSERT INTO btest VALUES('2019-01-20T09:00:43', '2018-01-20T09:00:55', 2, 30.5);
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
--check null cmp element is skipped
|
|
INSERT INTO btest VALUES('2018-01-20T09:00:43', NULL, 2, 32.3);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
-- fist returns NULL value
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
-- test first return non NULL value
|
|
INSERT INTO btest VALUES('2016-01-20T09:00:00', '2016-01-20T09:00:00', 2, 36.5);
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
--check non null cmp element insert after null cmp
|
|
INSERT INTO btest VALUES('2020-01-20T09:00:43', '2020-01-20T09:00:43', 2, 35.3);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
--cmp nulls should be ignored and not present in groups
|
|
:PREFIX SELECT gp, last(temp, time_alt) FROM btest GROUP BY gp ORDER BY gp;
|
|
--Previously, some bugs were found with NULLS and numeric types, so test that
|
|
INSERT INTO btest_numeric VALUES ('2019-01-20T09:00:43', NULL);
|
|
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
|
|
--check non-null element "overrides" NULL because it comes after.
|
|
INSERT INTO btest_numeric VALUES('2020-01-20T09:00:43', 30.5);
|
|
:PREFIX SELECT last(quantity, time) FROM btest_numeric;
|
|
-- do index scan for last
|
|
:PREFIX SELECT last(temp, time) FROM btest;
|
|
-- do index scan for first
|
|
:PREFIX SELECT first(temp, time) FROM btest;
|
|
-- can't do index scan when ordering on non-index column
|
|
:PREFIX SELECT first(temp, time_alt) FROM btest;
|
|
-- do index scan for subquery
|
|
:PREFIX SELECT * FROM (SELECT last(temp, time) FROM btest) last;
|
|
-- can't do index scan when using group by
|
|
:PREFIX SELECT last(temp, time) FROM btest GROUP BY gp ORDER BY gp;
|
|
-- do index scan when agg function is used in CTE subquery
|
|
:PREFIX WITH last_temp AS (SELECT last(temp, time) FROM btest) SELECT * from last_temp;
|
|
-- do index scan when using both FIRST and LAST aggregate functions
|
|
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
|
|
-- verify results when using both FIRST and LAST
|
|
:PREFIX SELECT first(temp, time), last(temp, time) FROM btest;
|
|
-- do index scan when using WHERE
|
|
:PREFIX SELECT last(temp, time) FROM btest WHERE time <= '2017-01-20T09:00:02';
|
|
-- can't do index scan for MAX and LAST combined (MinMax optimization fails when having different aggregate functions)
|
|
:PREFIX SELECT max(time), last(temp, time) FROM btest;
|
|
-- can't do index scan when using FIRST/LAST in ORDER BY
|
|
:PREFIX SELECT last(temp, time) FROM btest ORDER BY last(temp, time);
|
|
-- do index scan
|
|
:PREFIX SELECT last(temp, time) FROM btest WHERE temp < 30;
|
|
-- SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
|
|
-- do index scan
|
|
:PREFIX SELECT first(temp, time) FROM btest WHERE time >= '2017-01-20 09:00:47';
|
|
-- can't do index scan when using WINDOW function
|
|
:PREFIX SELECT gp, last(temp, time) OVER (PARTITION BY gp) AS last FROM btest;
|
|
-- test constants
|
|
:PREFIX SELECT first(100, 100) FROM btest;
|
|
-- create an index so we can test optimization
|
|
CREATE INDEX btest_time_alt_idx ON btest(time_alt);
|
|
:PREFIX SELECT last(temp, time_alt) FROM btest;
|
|
--test nested FIRST/LAST - should optimize
|
|
:PREFIX SELECT abs(last(temp, time)) FROM btest;
|
|
-- test nested FIRST/LAST in ORDER BY - no optimization possible
|
|
:PREFIX SELECT abs(last(temp, time)) FROM btest ORDER BY abs(last(temp,time));
|
|
ROLLBACK;
|
|
\o
|
|
:DIFF_CMD
|
|
--- Unoptimized result
|
|
+++ Optimized result
|
|
@@ -1,6 +1,6 @@
|
|
setting | value
|
|
----------------------------------+-------
|
|
- timescaledb.enable_optimizations | off
|
|
+ timescaledb.enable_optimizations | on
|
|
(1 row)
|
|
|
|
time | gp | temp
|