-- 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 max_parallel_workers_per_gather TO 0;
\set PREFIX 'EXPLAIN (buffers off, costs off) '
\ir include/plan_hashagg_load.sql
-- 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 metric (id SERIAL PRIMARY KEY, value INT);
CREATE TABLE hyper(time TIMESTAMP NOT NULL, time_int BIGINT, time_broken DATE, metricid int, value double precision);
CREATE TABLE regular(time TIMESTAMP NOT NULL, time_int BIGINT, time_date DATE, metricid int, value double precision);
SELECT create_hypertable('hyper', 'time', chunk_time_interval => interval '20 day', create_default_indexes=>FALSE);
psql:include/plan_hashagg_load.sql:9: WARNING:  column type "timestamp without time zone" used for "time" does not follow best practices
 create_hypertable  
--------------------
 (1,public,hyper,t)

ALTER TABLE hyper
DROP COLUMN time_broken,
ADD COLUMN time_date DATE;
INSERT INTO metric(value) SELECT random()*100 FROM generate_series(0,10);
INSERT INTO hyper SELECT t,  EXTRACT(EPOCH FROM t), (EXTRACT(EPOCH FROM t)::int % 10)+1, 1.0, t::date FROM generate_series('2001-01-01', '2001-01-10', INTERVAL '1 second') t;
INSERT INTO regular(time, time_int, time_date, metricid, value)
  SELECT t,  EXTRACT(EPOCH FROM t), t::date, (EXTRACT(EPOCH FROM t)::int % 10) + 1, 1.0 FROM generate_series('2001-01-01', '2001-01-02', INTERVAL '1 second') t;
--test some queries before analyze;
EXPLAIN (buffers off, costs off) SELECT time_bucket('1 minute', time) AS MetricMinuteTs, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 Sort
   Sort Key: (time_bucket('@ 1 min'::interval, _hyper_1_1_chunk."time")) DESC
   ->  HashAggregate
         Group Key: time_bucket('@ 1 min'::interval, _hyper_1_1_chunk."time")
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

EXPLAIN (buffers off, costs off) SELECT date_trunc('minute', time) AS MetricMinuteTs, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 Sort
   Sort Key: (date_trunc('minute'::text, _hyper_1_1_chunk."time")) DESC
   ->  HashAggregate
         Group Key: date_trunc('minute'::text, _hyper_1_1_chunk."time")
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

-- Test partitioning function on an open (time) dimension
CREATE OR REPLACE FUNCTION unix_to_timestamp(unixtime float8)
    RETURNS TIMESTAMPTZ LANGUAGE SQL IMMUTABLE AS
$BODY$
    SELECT to_timestamp(unixtime);
$BODY$;
CREATE TABLE hyper_timefunc(time float8 NOT NULL, metricid int, VALUE double precision, time_date DATE);
SELECT create_hypertable('hyper_timefunc', 'time', chunk_time_interval => interval '20 day', create_default_indexes=>FALSE, time_partitioning_func => 'unix_to_timestamp');
      create_hypertable      
-----------------------------
 (2,public,hyper_timefunc,t)

INSERT INTO hyper_timefunc SELECT time_int, metricid, VALUE, time_date FROM hyper;
ANALYZE metric;
ANALYZE hyper;
ANALYZE regular;
ANALYZE hyper_timefunc;
\ir include/plan_hashagg_query.sql
-- 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.
:PREFIX SELECT time_bucket('1 minute', time) AS MetricMinuteTs, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (time_bucket('@ 1 min'::interval, _hyper_1_1_chunk."time"))
   ->  Sort
         Sort Key: (time_bucket('@ 1 min'::interval, _hyper_1_1_chunk."time")) DESC
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

:PREFIX SELECT time_bucket('1 hour', time) AS MetricMinuteTs, metricid, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs, metricid
ORDER BY MetricMinuteTs DESC, metricid;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (time_bucket('@ 1 hour'::interval, _hyper_1_1_chunk."time")), _hyper_1_1_chunk.metricid
   ->  Sort
         Sort Key: (time_bucket('@ 1 hour'::interval, _hyper_1_1_chunk."time")) DESC, _hyper_1_1_chunk.metricid
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

--should be too many groups will not hashaggregate
:PREFIX SELECT time_bucket('1 second', time) AS MetricMinuteTs, metricid, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs, metricid
ORDER BY MetricMinuteTs DESC, metricid;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (time_bucket('@ 1 sec'::interval, _hyper_1_1_chunk."time")), _hyper_1_1_chunk.metricid
   ->  Sort
         Sort Key: (time_bucket('@ 1 sec'::interval, _hyper_1_1_chunk."time")) DESC, _hyper_1_1_chunk.metricid
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

:PREFIX SELECT time_bucket('1 minute', time, INTERVAL '30 seconds') AS MetricMinuteTs, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (time_bucket('@ 1 min'::interval, _hyper_1_1_chunk."time", '@ 30 secs'::interval))
   ->  Sort
         Sort Key: (time_bucket('@ 1 min'::interval, _hyper_1_1_chunk."time", '@ 30 secs'::interval)) DESC
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

:PREFIX SELECT time_bucket(60, time_int) AS MetricMinuteTs, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (time_bucket('60'::bigint, _hyper_1_1_chunk.time_int))
   ->  Sort
         Sort Key: (time_bucket('60'::bigint, _hyper_1_1_chunk.time_int)) DESC
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

:PREFIX SELECT time_bucket(60, time_int, 10) AS MetricMinuteTs, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (time_bucket('60'::bigint, _hyper_1_1_chunk.time_int, '10'::bigint))
   ->  Sort
         Sort Key: (time_bucket('60'::bigint, _hyper_1_1_chunk.time_int, '10'::bigint)) DESC
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

:PREFIX SELECT time_bucket('1 day', time_date) AS MetricMinuteTs, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 Sort
   Sort Key: (time_bucket('@ 1 day'::interval, _hyper_1_1_chunk.time_date)) DESC
   ->  HashAggregate
         Group Key: time_bucket('@ 1 day'::interval, _hyper_1_1_chunk.time_date)
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

:PREFIX SELECT date_trunc('minute', time) AS MetricMinuteTs, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (date_trunc('minute'::text, _hyper_1_1_chunk."time"))
   ->  Sort
         Sort Key: (date_trunc('minute'::text, _hyper_1_1_chunk."time")) DESC
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

\set ON_ERROR_STOP 0
--can't optimize invalid time unit
:PREFIX SELECT date_trunc('invalid', time) AS MetricMinuteTs, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (date_trunc('invalid'::text, _hyper_1_1_chunk."time"))
   ->  Sort
         Sort Key: (date_trunc('invalid'::text, _hyper_1_1_chunk."time")) DESC
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

\set ON_ERROR_STOP 1
:PREFIX SELECT date_trunc('day', time_date) AS MetricMinuteTs, AVG(value) as avg
FROM hyper
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 Sort
   Sort Key: (date_trunc('day'::text, (_hyper_1_1_chunk.time_date)::timestamp with time zone)) DESC
   ->  HashAggregate
         Group Key: date_trunc('day'::text, (_hyper_1_1_chunk.time_date)::timestamp with time zone)
         ->  Result
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))

--joins
--with hypertable, optimize
:PREFIX SELECT time_bucket(3600, time_int, 10) AS MetricMinuteTs, metric.value, AVG(hyper.value) as avg
FROM hyper
JOIN metric ON (hyper.metricid = metric.id)
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs, metric.id
ORDER BY MetricMinuteTs DESC, metric.id;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (time_bucket('3600'::bigint, _hyper_1_1_chunk.time_int, '10'::bigint)), metric.id
   ->  Sort
         Sort Key: (time_bucket('3600'::bigint, _hyper_1_1_chunk.time_int, '10'::bigint)) DESC, metric.id
         ->  Hash Join
               Hash Cond: (_hyper_1_1_chunk.metricid = metric.id)
               ->  Seq Scan on _hyper_1_1_chunk
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))
               ->  Hash
                     ->  Seq Scan on metric

--no hypertable involved, no optimization
:PREFIX SELECT time_bucket(3600, time_int, 10) AS MetricMinuteTs, metric.value, AVG(regular.value) as avg
FROM regular
JOIN metric ON (regular.metricid = metric.id)
WHERE time >= '2001-01-04T00:00:00' AND time <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs, metric.id
ORDER BY MetricMinuteTs DESC, metric.id;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (time_bucket('3600'::bigint, regular.time_int, '10'::bigint)), metric.id
   ->  Sort
         Sort Key: (time_bucket('3600'::bigint, regular.time_int, '10'::bigint)) DESC, metric.id
         ->  Nested Loop
               Join Filter: (regular.metricid = metric.id)
               ->  Seq Scan on regular
                     Filter: (("time" >= 'Thu Jan 04 00:00:00 2001'::timestamp without time zone) AND ("time" <= 'Fri Jan 05 01:00:00 2001'::timestamp without time zone))
               ->  Seq Scan on metric

-- Try with time partitioning function. Currently not optimized for hash aggregates
:PREFIX SELECT time_bucket('1 minute', unix_to_timestamp(time)) AS MetricMinuteTs, AVG(value) as avg
FROM hyper_timefunc
WHERE unix_to_timestamp(time) >= '2001-01-04T00:00:00' AND unix_to_timestamp(time) <= '2001-01-05T01:00:00'
GROUP BY MetricMinuteTs
ORDER BY MetricMinuteTs DESC;
--- QUERY PLAN ---
 GroupAggregate
   Group Key: (time_bucket('@ 1 min'::interval, to_timestamp(_hyper_2_2_chunk."time")))
   ->  Sort
         Sort Key: (time_bucket('@ 1 min'::interval, to_timestamp(_hyper_2_2_chunk."time"))) DESC
         ->  Result
               ->  Seq Scan on _hyper_2_2_chunk
                     Filter: ((to_timestamp("time") >= 'Thu Jan 04 00:00:00 2001 PST'::timestamp with time zone) AND (to_timestamp("time") <= 'Fri Jan 05 01:00:00 2001 PST'::timestamp with time zone))

\set ECHO none
psql:include/plan_hashagg_query.sql:60: ERROR:  unit "invalid" not recognized for type timestamp without time zone
psql:include/plan_hashagg_query.sql:60: ERROR:  unit "invalid" not recognized for type timestamp without time zone
