建议将带有聚合函数的动态 SQL 查询解析为 JSON 组数组
Advice parsing dynamic SQL query with aggregate functions into JSON set of arrays
我需要将 time_bucket
(TimescaleDB function derivated from postgres date_trunc) 的输出解析为数组的 JSON 对象。我是第一次这样做,经过大量研究,我得出了这样的结论:
CREATE OR REPLACE FUNCTION timescale.get_chronograph_json_time_bucket(
_ts_start timestamp without time zone,
_ts_end timestamp without time zone,
_y1_name text,
_y2_name text,
_tb_name text,
_time_bucket interval)
RETURNS json
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
DECLARE
chronograph json;
BEGIN
EXECUTE '
CREATE TEMP TABLE tmp_table ON COMMIT DROP AS
SELECT
time_bucket(,timestamp) AS bucket,
avg('|| quote_ident(_y1_name) ||') AS y1,
avg('|| quote_ident(_y2_name) ||') AS y2
FROM timescale.' || quote_ident(_tb_name) ||'
WHERE timestamp BETWEEN AND
GROUP BY bucket ORDER BY bucket'
USING _ts_start, _ts_end, _time_bucket;
SELECT json_build_object(
'ts',json_agg(bucket),
'y1',json_agg(y1),
'y2',json_agg(y2))
FROM tmp_table
INTO chronograph;
RETURN chronograph;
END
$BODY$;
它return是指定时间戳范围(_ts_start
、[=17=)内用户请求变量(_y1_name
、_y2_name
)的所有行]) 与请求的项目 (_tb_name
) 请求的分辨率 (_time_bucket
)。
它正在做这项工作,但有一些“问题”困扰着我:
- 是否有必要创建临时table?如果有两个同时调用函数怎么办,他们不会共享相同的时间 table?
-
INTO chronograph; RETURN chronograph;
看起来也有点奇怪。没有更直接的方法 return JSON 对象?
- 这个例子有两个变量的请求,但是,我如何概括这个方案才能回答从 1 到 n 个变量的请求?
不,没有必要创建临时 table。是的,execute '...' into some_variable
是将(动态)SQL 语句的结果存储到变量中的唯一方法。
构建动态时 SQL 通常最好使用 format()
函数。它使实际的 SQL 更易于阅读,并且还可以正确处理标识符。
可以使用不同的标签嵌套美元引号字符串,这使得在实际 SQL 字符串中嵌入单引号变得更加容易。
CREATE OR REPLACE FUNCTION timescale.get_chronograph_json_time_bucket(
_ts_start timestamp without time zone,
_ts_end timestamp without time zone,
_y1_name text,
_y2_name text,
_tb_name text,
_time_bucket interval)
RETURNS json
LANGUAGE plpgsql
AS
$BODY$
DECLARE
chronograph json;
BEGIN
EXECUTE format($sql$
SELECT json_build_object(
'ts',json_agg(bucket),
'y1',json_agg(y1),
'y2',json_agg(y2))
FROM (
SELECT
time_bucket(,timestamp) AS bucket,
avg(%I) AS y1,
avg(%I) AS y2
FROM timescale.%I
WHERE timestamp BETWEEN AND
GROUP BY bucket ORDER BY bucket
) tmp
$sql$, _y1_name, _y2_name, _tb_name)
INTO chronograph
USING _ts_start, _ts_end, _time_bucket;
RETURN chronograph;
END
$BODY$;
理论上,这可以扩展为遍历列名列表并在该循环中构建整个 SQL 字符串以处理可变的列列表。
我需要将 time_bucket
(TimescaleDB function derivated from postgres date_trunc) 的输出解析为数组的 JSON 对象。我是第一次这样做,经过大量研究,我得出了这样的结论:
CREATE OR REPLACE FUNCTION timescale.get_chronograph_json_time_bucket(
_ts_start timestamp without time zone,
_ts_end timestamp without time zone,
_y1_name text,
_y2_name text,
_tb_name text,
_time_bucket interval)
RETURNS json
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
DECLARE
chronograph json;
BEGIN
EXECUTE '
CREATE TEMP TABLE tmp_table ON COMMIT DROP AS
SELECT
time_bucket(,timestamp) AS bucket,
avg('|| quote_ident(_y1_name) ||') AS y1,
avg('|| quote_ident(_y2_name) ||') AS y2
FROM timescale.' || quote_ident(_tb_name) ||'
WHERE timestamp BETWEEN AND
GROUP BY bucket ORDER BY bucket'
USING _ts_start, _ts_end, _time_bucket;
SELECT json_build_object(
'ts',json_agg(bucket),
'y1',json_agg(y1),
'y2',json_agg(y2))
FROM tmp_table
INTO chronograph;
RETURN chronograph;
END
$BODY$;
它return是指定时间戳范围(_ts_start
、[=17=)内用户请求变量(_y1_name
、_y2_name
)的所有行]) 与请求的项目 (_tb_name
) 请求的分辨率 (_time_bucket
)。
它正在做这项工作,但有一些“问题”困扰着我:
- 是否有必要创建临时table?如果有两个同时调用函数怎么办,他们不会共享相同的时间 table?
-
INTO chronograph; RETURN chronograph;
看起来也有点奇怪。没有更直接的方法 return JSON 对象? - 这个例子有两个变量的请求,但是,我如何概括这个方案才能回答从 1 到 n 个变量的请求?
不,没有必要创建临时 table。是的,execute '...' into some_variable
是将(动态)SQL 语句的结果存储到变量中的唯一方法。
构建动态时 SQL 通常最好使用 format()
函数。它使实际的 SQL 更易于阅读,并且还可以正确处理标识符。
可以使用不同的标签嵌套美元引号字符串,这使得在实际 SQL 字符串中嵌入单引号变得更加容易。
CREATE OR REPLACE FUNCTION timescale.get_chronograph_json_time_bucket(
_ts_start timestamp without time zone,
_ts_end timestamp without time zone,
_y1_name text,
_y2_name text,
_tb_name text,
_time_bucket interval)
RETURNS json
LANGUAGE plpgsql
AS
$BODY$
DECLARE
chronograph json;
BEGIN
EXECUTE format($sql$
SELECT json_build_object(
'ts',json_agg(bucket),
'y1',json_agg(y1),
'y2',json_agg(y2))
FROM (
SELECT
time_bucket(,timestamp) AS bucket,
avg(%I) AS y1,
avg(%I) AS y2
FROM timescale.%I
WHERE timestamp BETWEEN AND
GROUP BY bucket ORDER BY bucket
) tmp
$sql$, _y1_name, _y2_name, _tb_name)
INTO chronograph
USING _ts_start, _ts_end, _time_bucket;
RETURN chronograph;
END
$BODY$;
理论上,这可以扩展为遍历列名列表并在该循环中构建整个 SQL 字符串以处理可变的列列表。