Support seconds, minutes, and hours in time_bucket_ng()

As a future replacement for time_bucket(), time_bucket_ng()
should support seconds, minutes, and hours. This patch adds
this support. The implementation is the same as for
time_bucket(). Timezones are not yet supported.
This commit is contained in:
Aleksander Alekseev 2021-07-06 15:57:36 +03:00 committed by Aleksander Alekseev
parent 78a21f412b
commit 99f7a2122f
10 changed files with 376 additions and 173 deletions

View File

@ -24,10 +24,10 @@
-- [2]: https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-TIMEZONES
--
CREATE OR REPLACE FUNCTION timescaledb_experimental.time_bucket_ng(bucket_width INTERVAL, ts DATE) RETURNS DATE
AS '@MODULE_PATHNAME@', 'ts_time_bucket_ng' LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT;
AS '@MODULE_PATHNAME@', 'ts_time_bucket_ng_date' LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT;
CREATE OR REPLACE FUNCTION timescaledb_experimental.time_bucket_ng(bucket_width INTERVAL, ts DATE, origin DATE) RETURNS DATE
AS '@MODULE_PATHNAME@', 'ts_time_bucket_ng' LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT;
AS '@MODULE_PATHNAME@', 'ts_time_bucket_ng_date' LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT;
-- utility functions
CREATE OR REPLACE FUNCTION timescaledb_experimental.time_bucket_ng(bucket_width INTERVAL, ts TIMESTAMP) RETURNS TIMESTAMP

View File

@ -48,7 +48,6 @@ set(SOURCES
subspace_store.c
tablespace.c
time_bucket.c
time_bucket_ng.c
time_utils.c
custom_type_cache.c
trigger.c

View File

@ -292,3 +292,168 @@ ts_time_bucket_by_type(int64 interval, int64 timestamp, Oid timestamp_type)
return ts_time_value_to_internal(time_bucketed, timestamp_type);
}
TS_FUNCTION_INFO_V1(ts_time_bucket_ng_timestamp);
TSDLLEXPORT Datum
ts_time_bucket_ng_timestamp(PG_FUNCTION_ARGS)
{
DateADT ts_date;
Interval *interval = PG_GETARG_INTERVAL_P(0);
Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
if (interval->time != 0)
{
if (interval->month != 0)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("interval can't combine months with minutes or hours")));
}
/*
* Handle minutes, hours and days.
*/
Timestamp origin = (PG_NARGS() > 2 ? PG_GETARG_TIMESTAMP(2) : DEFAULT_ORIGIN);
Timestamp result;
int64 period = get_interval_period_timestamp_units(interval);
if (TIMESTAMP_NOT_FINITE(timestamp))
PG_RETURN_TIMESTAMP(timestamp);
TIME_BUCKET_TS(period, timestamp, result, origin);
PG_RETURN_TIMESTAMP(result);
}
/*
* Discard any time information and work with the date.
*/
ts_date = DatumGetDateADT(DirectFunctionCall1(timestamp_date, PG_GETARG_DATUM(1)));
if (PG_NARGS() > 2)
{
DateADT result;
DateADT origin = DatumGetDateADT(DirectFunctionCall1(timestamp_date, PG_GETARG_DATUM(2)));
result = DatumGetDateADT(DirectFunctionCall3(ts_time_bucket_ng_date,
PG_GETARG_DATUM(0),
DateADTGetDatum(ts_date),
DateADTGetDatum(origin)));
return DirectFunctionCall1(date_timestamp, DateADTGetDatum(result));
}
else
{
DateADT result;
result = DatumGetDateADT(DirectFunctionCall2(ts_time_bucket_ng_date,
PG_GETARG_DATUM(0),
DateADTGetDatum(ts_date)));
return DirectFunctionCall1(date_timestamp, DateADTGetDatum(result));
}
}
TS_FUNCTION_INFO_V1(ts_time_bucket_ng_timestamptz);
TSDLLEXPORT Datum
ts_time_bucket_ng_timestamptz(PG_FUNCTION_ARGS)
{
DateADT result;
Datum interval = PG_GETARG_DATUM(0);
DateADT ts_date = DatumGetDateADT(DirectFunctionCall1(timestamptz_date, PG_GETARG_DATUM(1)));
if (PG_NARGS() > 2)
{
DateADT origin = DatumGetDateADT(DirectFunctionCall1(timestamptz_date, PG_GETARG_DATUM(2)));
result = DatumGetDateADT(DirectFunctionCall3(ts_time_bucket_ng_date,
interval,
DateADTGetDatum(ts_date),
DateADTGetDatum(origin)));
}
else
{
result = DatumGetDateADT(
DirectFunctionCall2(ts_time_bucket_ng_date, interval, DateADTGetDatum(ts_date)));
}
return DirectFunctionCall1(date_timestamptz, DateADTGetDatum(result));
}
TS_FUNCTION_INFO_V1(ts_time_bucket_ng_date);
TSDLLEXPORT Datum
ts_time_bucket_ng_date(PG_FUNCTION_ARGS)
{
Interval *interval = PG_GETARG_INTERVAL_P(0);
DateADT date = PG_GETARG_DATEADT(1);
DateADT origin_date = 0;
int origin_year = 2000, origin_month = 1, origin_day = 1;
int year, month, day;
int delta, bucket_number;
if ((interval->time != 0) || ((interval->month != 0) && (interval->day != 0)))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("interval must be either days and weeks, or months and years")));
}
if ((interval->month == 0) && (interval->day == 0))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("interval must be at least one day")));
}
if (PG_NARGS() > 2)
{
origin_date = PG_GETARG_DATUM(2);
j2date(origin_date + POSTGRES_EPOCH_JDATE, &origin_year, &origin_month, &origin_day);
}
if ((origin_day != 1) && (interval->month != 0))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("origin must be the first day of the month")));
}
if (DATE_NOT_FINITE(date))
PG_RETURN_DATEADT(date);
if (interval->month != 0)
{
/* Handle months and years */
j2date(date + POSTGRES_EPOCH_JDATE, &year, &month, &day);
if ((year < origin_year) || ((year == origin_year) && (month < origin_month)))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("origin must be before the given date")));
}
delta = (year * 12 + month) - (origin_year * 12 + origin_month);
bucket_number = delta / interval->month;
year = origin_year + (bucket_number * interval->month) / 12;
month =
(((origin_year * 12 + (origin_month - 1)) + (bucket_number * interval->month)) % 12) +
1;
day = 1;
date = date2j(year, month, day) - POSTGRES_EPOCH_JDATE;
}
else
{
/* Handle days and weeks */
if (date < origin_date)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("origin must be before the given date")));
}
delta = date - origin_date;
bucket_number = delta / interval->day;
date = bucket_number * interval->day;
}
PG_RETURN_DATEADT(date);
}

View File

@ -18,5 +18,8 @@ extern TSDLLEXPORT Datum ts_date_bucket(PG_FUNCTION_ARGS);
extern TSDLLEXPORT Datum ts_timestamp_bucket(PG_FUNCTION_ARGS);
extern TSDLLEXPORT Datum ts_timestamptz_bucket(PG_FUNCTION_ARGS);
extern TSDLLEXPORT int64 ts_time_bucket_by_type(int64 interval, int64 timestamp, Oid type);
extern TSDLLEXPORT Datum ts_time_bucket_ng_date(PG_FUNCTION_ARGS);
extern TSDLLEXPORT Datum ts_time_bucket_ng_timestamp(PG_FUNCTION_ARGS);
extern TSDLLEXPORT Datum ts_time_bucket_ng_timestamptz(PG_FUNCTION_ARGS);
#endif /* TIMESCALEDB_TIME_BUCKET_H */

View File

@ -1,152 +0,0 @@
/*
* 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.
*/
#include <postgres.h>
#include <utils/date.h>
#include <utils/datetime.h>
#include <utils/fmgrprotos.h>
#include "time_bucket_ng.h"
TS_FUNCTION_INFO_V1(ts_time_bucket_ng);
TS_FUNCTION_INFO_V1(ts_time_bucket_ng_timestamp);
TS_FUNCTION_INFO_V1(ts_time_bucket_ng_timestamptz);
TSDLLEXPORT Datum
ts_time_bucket_ng_timestamp(PG_FUNCTION_ARGS)
{
DateADT result;
Datum interval = PG_GETARG_DATUM(0);
DateADT ts_date = DatumGetDateADT(DirectFunctionCall1(timestamp_date, PG_GETARG_DATUM(1)));
if (PG_NARGS() > 2)
{
DateADT origin = DatumGetDateADT(DirectFunctionCall1(timestamp_date, PG_GETARG_DATUM(2)));
result = DatumGetDateADT(DirectFunctionCall3(ts_time_bucket_ng,
interval,
DateADTGetDatum(ts_date),
DateADTGetDatum(origin)));
}
else
{
result = DatumGetDateADT(
DirectFunctionCall2(ts_time_bucket_ng, interval, DateADTGetDatum(ts_date)));
}
return DirectFunctionCall1(date_timestamp, DateADTGetDatum(result));
}
TSDLLEXPORT Datum
ts_time_bucket_ng_timestamptz(PG_FUNCTION_ARGS)
{
DateADT result;
Datum interval = PG_GETARG_DATUM(0);
DateADT ts_date = DatumGetDateADT(DirectFunctionCall1(timestamptz_date, PG_GETARG_DATUM(1)));
if (PG_NARGS() > 2)
{
DateADT origin = DatumGetDateADT(DirectFunctionCall1(timestamptz_date, PG_GETARG_DATUM(2)));
result = DatumGetDateADT(DirectFunctionCall3(ts_time_bucket_ng,
interval,
DateADTGetDatum(ts_date),
DateADTGetDatum(origin)));
}
else
{
result = DatumGetDateADT(
DirectFunctionCall2(ts_time_bucket_ng, interval, DateADTGetDatum(ts_date)));
}
return DirectFunctionCall1(date_timestamptz, DateADTGetDatum(result));
}
TSDLLEXPORT Datum
ts_time_bucket_ng(PG_FUNCTION_ARGS)
{
Interval *interval = PG_GETARG_INTERVAL_P(0);
DateADT date = PG_GETARG_DATEADT(1);
DateADT origin_date = 0; // 2000-01-01
int origin_year = 2000, origin_month = 1, origin_day = 1;
int year, month, day;
int delta, bucket_number;
if ((interval->time != 0) || ((interval->month != 0) && (interval->day != 0)))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("interval must be either days and weeks, or months and years")));
}
if ((interval->month == 0) && (interval->day == 0))
{
/*
* This will be fixed in future versions of ts_time_bucket_ng().
* The reason why it's not yet implemented is that we want to start
* experimenting with variable-sized buckets as soon as possible.
* We know that fixed-sized buckets work OK and adding corresponding
* logic will be trivial.
*/
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("interval must be at least one day")));
}
if (PG_NARGS() > 2)
{
origin_date = PG_GETARG_DATUM(2);
j2date(origin_date + POSTGRES_EPOCH_JDATE, &origin_year, &origin_month, &origin_day);
}
if ((origin_day != 1) && (interval->month != 0))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("origin must be the first day of the month")));
}
if (DATE_NOT_FINITE(date))
PG_RETURN_DATEADT(date);
if (interval->month != 0)
{
/* Handle months and years */
j2date(date + POSTGRES_EPOCH_JDATE, &year, &month, &day);
if ((year < origin_year) || ((year == origin_year) && (month < origin_month)))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("origin must be before the given date")));
}
delta = (year * 12 + month) - (origin_year * 12 + origin_month);
bucket_number = delta / interval->month;
year = origin_year + (bucket_number * interval->month) / 12;
month =
(((origin_year * 12 + (origin_month - 1)) + (bucket_number * interval->month)) % 12) +
1;
day = 1;
date = date2j(year, month, day) - POSTGRES_EPOCH_JDATE;
}
else
{
/* Handle days and weeks */
if (date < origin_date)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("origin must be before the given date")));
}
delta = date - origin_date;
bucket_number = delta / interval->day;
date = bucket_number * interval->day;
}
PG_RETURN_DATEADT(date);
}

View File

@ -1,18 +0,0 @@
/*
* 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.
*/
#ifndef TIMESCALEDB_DATE_TRUNC_H
#define TIMESCALEDB_DATE_TRUNC_H
#include <postgres.h>
#include <fmgr.h>
#include "export.h"
extern TSDLLEXPORT Datum ts_time_bucket_ng(PG_FUNCTION_ARGS);
extern TSDLLEXPORT Datum ts_time_bucket_ng_timestamp(PG_FUNCTION_ARGS);
extern TSDLLEXPORT Datum ts_time_bucket_ng_timestamptz(PG_FUNCTION_ARGS);
#endif /* TIMESCALEDB_DATE_TRUNC_H */

View File

@ -1238,6 +1238,8 @@ SELECT timescaledb_experimental.time_bucket_ng('1 month', '2000-01-02' :: date,
ERROR: origin must be before the given date
SELECT timescaledb_experimental.time_bucket_ng('1 day', '2000-01-02' :: date, origin => '2001-01-01') AS result;
ERROR: origin must be before the given date
SELECT timescaledb_experimental.time_bucket_ng('1 month 3 hours', '2021-11-22' :: timestamp) AS result;
ERROR: interval can't combine months with minutes or hours
\set ON_ERROR_STOP 1
-- infinity
SELECT timescaledb_experimental.time_bucket_ng('1 year', 'infinity' :: date) AS result;
@ -1271,6 +1273,48 @@ SELECT timescaledb_experimental.time_bucket_ng('1 year', '2021-11-22' :: timesta
Tue Jun 01 00:00:00 2021 EDT
(1 row)
-- Make sure time_bucket_ng() supports seconds, minutes, and hours.
-- We happen to know that the internal implementation is the same
-- as for time_bucket(), thus there is no reason to execute all the tests
-- we already have for time_bucket(). These two functions will most likely
-- be merged eventually anyway.
SELECT timescaledb_experimental.time_bucket_ng('30 seconds', '2021-07-12 12:34:56' :: timestamp) AS result;
result
--------------------------
Mon Jul 12 12:34:30 2021
(1 row)
SELECT timescaledb_experimental.time_bucket_ng('15 minutes', '2021-07-12 12:34:56' :: timestamp) AS result;
result
--------------------------
Mon Jul 12 12:30:00 2021
(1 row)
SELECT timescaledb_experimental.time_bucket_ng('6 hours', '2021-07-12 12:34:56' :: timestamp) AS result;
result
--------------------------
Mon Jul 12 12:00:00 2021
(1 row)
-- Same as above, but with provided 'origin' argument.
SELECT timescaledb_experimental.time_bucket_ng('30 seconds', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result;
result
--------------------------
Mon Jul 12 12:34:30 2021
(1 row)
SELECT timescaledb_experimental.time_bucket_ng('15 minutes', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result;
result
--------------------------
Mon Jul 12 12:25:00 2021
(1 row)
SELECT timescaledb_experimental.time_bucket_ng('6 hours', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result;
result
--------------------------
Mon Jul 12 12:10:00 2021
(1 row)
-- N days / weeks buckets
SELECT to_char(d, 'YYYY-MM-DD') AS d,
to_char(timescaledb_experimental.time_bucket_ng('1 day', d), 'YYYY-MM-DD') AS d1,

View File

@ -609,6 +609,7 @@ SELECT timescaledb_experimental.time_bucket_ng('0 days', '2001-02-03' :: date) A
SELECT timescaledb_experimental.time_bucket_ng('1 month', '2001-02-03' :: date, origin => '2000-01-02') AS result;
SELECT timescaledb_experimental.time_bucket_ng('1 month', '2000-01-02' :: date, origin => '2001-01-01') AS result;
SELECT timescaledb_experimental.time_bucket_ng('1 day', '2000-01-02' :: date, origin => '2001-01-01') AS result;
SELECT timescaledb_experimental.time_bucket_ng('1 month 3 hours', '2021-11-22' :: timestamp) AS result;
\set ON_ERROR_STOP 1
-- infinity
@ -620,6 +621,20 @@ SELECT timescaledb_experimental.time_bucket_ng('1 year', '2021-11-22' :: timesta
SELECT timescaledb_experimental.time_bucket_ng('1 year', '2021-11-22' :: timestamp, origin => '2021-06-01') AS result;
SELECT timescaledb_experimental.time_bucket_ng('1 year', '2021-11-22' :: timestamptz, origin => '2021-06-01') AS result;
-- Make sure time_bucket_ng() supports seconds, minutes, and hours.
-- We happen to know that the internal implementation is the same
-- as for time_bucket(), thus there is no reason to execute all the tests
-- we already have for time_bucket(). These two functions will most likely
-- be merged eventually anyway.
SELECT timescaledb_experimental.time_bucket_ng('30 seconds', '2021-07-12 12:34:56' :: timestamp) AS result;
SELECT timescaledb_experimental.time_bucket_ng('15 minutes', '2021-07-12 12:34:56' :: timestamp) AS result;
SELECT timescaledb_experimental.time_bucket_ng('6 hours', '2021-07-12 12:34:56' :: timestamp) AS result;
-- Same as above, but with provided 'origin' argument.
SELECT timescaledb_experimental.time_bucket_ng('30 seconds', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result;
SELECT timescaledb_experimental.time_bucket_ng('15 minutes', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result;
SELECT timescaledb_experimental.time_bucket_ng('6 hours', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result;
-- N days / weeks buckets
SELECT to_char(d, 'YYYY-MM-DD') AS d,
to_char(timescaledb_experimental.time_bucket_ng('1 day', d), 'YYYY-MM-DD') AS d1,

View File

@ -53,3 +53,89 @@ ORDER BY bucket;
DROP TABLE conditions CASCADE;
NOTICE: drop cascades to 3 other objects
NOTICE: drop cascades to 2 other objects
-- Make sure seconds, minutes, and hours can be used with caggs ('origin' is not
-- currently supported in caggs).
CREATE TABLE conditions(
tstamp TIMESTAMP NOT NULL,
city text NOT NULL,
temperature INT NOT NULL);
SELECT create_hypertable(
'conditions', 'tstamp',
chunk_time_interval => INTERVAL '1 day'
);
create_hypertable
-------------------------
(3,public,conditions,t)
(1 row)
INSERT INTO conditions (tstamp, city, temperature) VALUES
('2021-06-14 12:30:00', 'Moscow', 26),
('2021-06-14 12:30:10', 'Moscow', 22),
('2021-06-14 12:30:20', 'Moscow', 24),
('2021-06-14 12:30:30', 'Moscow', 24),
('2021-06-14 12:30:40', 'Moscow', 27),
('2021-06-14 12:30:50', 'Moscow', 28),
('2021-06-14 12:31:10', 'Moscow', 30),
('2021-06-14 12:31:20', 'Moscow', 31),
('2021-06-14 12:31:30', 'Moscow', 34),
('2021-06-14 12:31:40', 'Moscow', 34),
('2021-06-14 12:31:50', 'Moscow', 34),
('2021-06-14 12:32:00', 'Moscow', 32),
('2021-06-14 12:32:10', 'Moscow', 32),
('2021-06-14 12:32:20', 'Moscow', 31);
CREATE MATERIALIZED VIEW conditions_summary_30sec
WITH (timescaledb.continuous) AS
SELECT city,
timescaledb_experimental.time_bucket_ng('30 seconds', tstamp) AS bucket,
MIN(temperature),
MAX(temperature)
FROM conditions
GROUP BY city, bucket;
NOTICE: refreshing continuous aggregate "conditions_summary_30sec"
CREATE MATERIALIZED VIEW conditions_summary_1min
WITH (timescaledb.continuous) AS
SELECT city,
timescaledb_experimental.time_bucket_ng('1 minute', tstamp) AS bucket,
MIN(temperature),
MAX(temperature)
FROM conditions
GROUP BY city, bucket;
NOTICE: refreshing continuous aggregate "conditions_summary_1min"
CREATE MATERIALIZED VIEW conditions_summary_1hour
WITH (timescaledb.continuous) AS
SELECT city,
timescaledb_experimental.time_bucket_ng('1 hour', tstamp) AS bucket,
MIN(temperature),
MAX(temperature)
FROM conditions
GROUP BY city, bucket;
NOTICE: refreshing continuous aggregate "conditions_summary_1hour"
SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_30sec ORDER BY bucket;
city | to_char | min | max
--------+---------------------+-----+-----
Moscow | 2021-06-14 12:30:00 | 22 | 26
Moscow | 2021-06-14 12:30:30 | 24 | 28
Moscow | 2021-06-14 12:31:00 | 30 | 31
Moscow | 2021-06-14 12:31:30 | 34 | 34
Moscow | 2021-06-14 12:32:00 | 31 | 32
(5 rows)
SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_1min ORDER BY bucket;
city | to_char | min | max
--------+---------------------+-----+-----
Moscow | 2021-06-14 12:30:00 | 22 | 28
Moscow | 2021-06-14 12:31:00 | 30 | 34
Moscow | 2021-06-14 12:32:00 | 31 | 32
(3 rows)
SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_1hour ORDER BY bucket;
city | to_char | min | max
--------+---------------------+-----+-----
Moscow | 2021-06-14 12:00:00 | 22 | 34
(1 row)
DROP TABLE conditions CASCADE;
NOTICE: drop cascades to 9 other objects
NOTICE: drop cascades to table _timescaledb_internal._hyper_4_18_chunk
NOTICE: drop cascades to table _timescaledb_internal._hyper_5_19_chunk
NOTICE: drop cascades to table _timescaledb_internal._hyper_6_20_chunk

View File

@ -45,3 +45,64 @@ FROM conditions_summary_weekly
ORDER BY bucket;
DROP TABLE conditions CASCADE;
-- Make sure seconds, minutes, and hours can be used with caggs ('origin' is not
-- currently supported in caggs).
CREATE TABLE conditions(
tstamp TIMESTAMP NOT NULL,
city text NOT NULL,
temperature INT NOT NULL);
SELECT create_hypertable(
'conditions', 'tstamp',
chunk_time_interval => INTERVAL '1 day'
);
INSERT INTO conditions (tstamp, city, temperature) VALUES
('2021-06-14 12:30:00', 'Moscow', 26),
('2021-06-14 12:30:10', 'Moscow', 22),
('2021-06-14 12:30:20', 'Moscow', 24),
('2021-06-14 12:30:30', 'Moscow', 24),
('2021-06-14 12:30:40', 'Moscow', 27),
('2021-06-14 12:30:50', 'Moscow', 28),
('2021-06-14 12:31:10', 'Moscow', 30),
('2021-06-14 12:31:20', 'Moscow', 31),
('2021-06-14 12:31:30', 'Moscow', 34),
('2021-06-14 12:31:40', 'Moscow', 34),
('2021-06-14 12:31:50', 'Moscow', 34),
('2021-06-14 12:32:00', 'Moscow', 32),
('2021-06-14 12:32:10', 'Moscow', 32),
('2021-06-14 12:32:20', 'Moscow', 31);
CREATE MATERIALIZED VIEW conditions_summary_30sec
WITH (timescaledb.continuous) AS
SELECT city,
timescaledb_experimental.time_bucket_ng('30 seconds', tstamp) AS bucket,
MIN(temperature),
MAX(temperature)
FROM conditions
GROUP BY city, bucket;
CREATE MATERIALIZED VIEW conditions_summary_1min
WITH (timescaledb.continuous) AS
SELECT city,
timescaledb_experimental.time_bucket_ng('1 minute', tstamp) AS bucket,
MIN(temperature),
MAX(temperature)
FROM conditions
GROUP BY city, bucket;
CREATE MATERIALIZED VIEW conditions_summary_1hour
WITH (timescaledb.continuous) AS
SELECT city,
timescaledb_experimental.time_bucket_ng('1 hour', tstamp) AS bucket,
MIN(temperature),
MAX(temperature)
FROM conditions
GROUP BY city, bucket;
SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_30sec ORDER BY bucket;
SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_1min ORDER BY bucket;
SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_1hour ORDER BY bucket;
DROP TABLE conditions CASCADE;