如果我拥有与 本问题 中相同的示例数据,并另外声明了以下两个函数:
创建或替换函数 example.markout_666_example_666_price_table_666_price(_symbol text, _time_of timestamptz, _start interval, _duration interval)
返回 float8
LANGUAGE sql STABLE STRICT PARALLEL SAFE AS -- !
$func$
SELECT p.price
FROM example.price_table p
WHERE p.symbol = _symbol
AND p.time_of >= _time_of + _start
AND p.time_of <= _time_of + _start + _duration
ORDER BY p.time_of
LIMIT 1;
$func$;
CREATE OR REPLACE FUNCTION example.markout_666_example_666_price_table_666_volume(_symbol text, _time_of timestamptz, _start interval, _duration interval)
返回 float8
LANGUAGE sql STABLE STRICT PARALLEL SAFE AS -- !
$func$
SELECT p.volume
FROM example.price_table p
WHERE p.symbol = _symbol
AND p.time_of >= _time_of + _start
AND p.time_of <= _time_of + _start + _duration
ORDER BY p.time_of
LIMIT 1;
$func$;
这两个函数类似,但引用的列不同。在更普遍的情况下,它们也可能引用不同的表。不过,我之所以说明这两个不同的函数,是因为在函数中输入列名(或不同的表名)似乎被认为是编写 Postgres 函数的一种反模式。
我可以在以下查询中使用这两个函数:
SELECT symbol, time_of, example.markout_666_example_666_price_table_666_price(symbol, time_of, '3 hours', '24 hours') as markout_price、
example.markout_666_example_666_price_table_666_price(symbol, time_of, '25 hours', '24 hours') 作为 markout_price_2、
example.markout_666_example_666_price_table_666_volume(symbol, time_of, '3 hours', '24 hours') as markout_volume
from example.interesting_times it;
然而,这相当冗长,我们需要多次写入 symbol 和 time_of。如果我们为更多的表声明函数,并为这些表声明更多的函数,查询可能会变得相当复杂。 是否可以改写如下内容:
SELECT symbol, time_of, example.markout('example.price_table', 'price', '3 hours', '24 hours') as markout_price、
example.markout('example.price_table', 'price', '25 小时', '24 小时') 作为 markout_price_2、
example.markout('example.price_table', 'volume', '3 hours', '24 hours') 作为 markout_volume
from example.interesting_times it;
其中
example.markout
是一个宏/元编程类型的构造,那么该函数的评估方式是否与我们使用更详细的语法相同?这里是否可以使用任何类似于元编程的技术?
我所能搜索到的只有 oracle 数据库中的 sql_macro ,以及过时版本 Postgres 中关于 "macro commands" 的 此页面 ,而 Postgres 手册中已不再有此页面。
虽然 PostgreSQL 中没有内置像 Oracle 的
sql_macro
那样的宏系统,但是可以通过以下几种方式来实现类似的功能,以避免重复代码并提高可读性:
1. 动态 SQL (PL/pgSQL 函数)
创建一个 PL/pgSQL 函数,该函数接受表名和列名作为参数,并使用动态 SQL 构造和执行查询:
CREATE OR REPLACE FUNCTION example.markout(
_table_name text,
_column_name text,
_symbol text,
_time_of timestamptz,
_start interval,
_duration interval
) RETURNS float8
LANGUAGE plpgsql STABLE STRICT PARALLEL SAFE AS
$func$
DECLARE
_sql text;
_result float8;
BEGIN
_sql := format('
SELECT %I
FROM %I
WHERE symbol = $1
AND time_of >= $2 + $3
AND time_of <= $2 + $3 + $4
ORDER BY time_of
LIMIT 1',
_column_name,
_table_name
);
EXECUTE _sql INTO _result USING _symbol, _time_of, _start, _duration;
RETURN _result;
END;
$func$;
然后,可以像这样调用该函数:
SELECT symbol, time_of,
example.markout('example.price_table', 'price', symbol, time_of, '3 hours', '24 hours') as markout_price,
example.markout('example.price_table', 'price', symbol, time_of, '25 hours', '24 hours') as markout_price_2,
example.markout('example.price_table', 'volume', symbol, time_of, '3 hours', '24 hours') as markout_volume
FROM example.interesting_times it;
2. 视图和函数组合
创建多个视图,每个视图代表一个特定的表和列组合,然后创建一个通用的函数来查询这些视图:
-- 创建视图
CREATE VIEW example.price_table_price AS
SELECT symbol, time_of, price
FROM example.price_table;
CREATE VIEW example.price_table_volume AS
SELECT symbol, time_of, volume
FROM example.price_table;
-- 创建通用函数
CREATE OR REPLACE FUNCTION example.markout_view(
_view_name text,
_symbol text,
_time_of timestamptz,
_start interval,
_duration interval
) RETURNS float8
LANGUAGE sql STABLE STRICT PARALLEL SAFE AS
$func$
SELECT CASE
WHEN _view_name = 'example.price_table_price' THEN price
WHEN _view_name = 'example.price_table_volume' THEN volume
END
FROM _view_name
WHERE symbol = _symbol
AND time_of >= _time_of + _start
AND time_of <= _time_of + _start + _duration
ORDER BY time_of
LIMIT 1;
$func$;
-- 调用函数
SELECT symbol, time_of,
example.markout_view('example.price_table_price', symbol, time_of, '3 hours', '24 hours') as markout_price,
example.markout_view('example.price_table_price', symbol, time_of, '25 hours', '24 hours') as markout_price_2,
example.markout_view('example.price_table_volume', symbol, time_of, '3 hours', '24 hours') as markout_volume
FROM example.interesting_times it;
这种方法可以避免动态 SQL 的一些开销,但需要为每个表和列组合创建视图。
3. 使用 ORM 或查询构建器
如果使用的是 ORM(对象关系映射)库或查询构建器,它们通常提供更简洁和灵活的方式来构建和执行查询,从而避免重复代码。
选择最佳方法取决于的具体需求和代码库的结构。动态 SQL 提供最大的灵活性,而视图和函数组合更易于理解和维护,但灵活性较差。ORM 和查询构建器提供了一种更高级别的抽象,可以简化代码,但需要学习和使用额外的工具。
标签:sql,postgresql,plpgsql,dynamic-sql From: 78589503