首页 > 编程问答 >Postgres 查询中的宏/元编程

Postgres 查询中的宏/元编程

时间:2024-06-08 21:59:52浏览次数:18  
标签:sql postgresql plpgsql dynamic-sql

如果我拥有与 本问题 中相同的示例数据,并另外声明了以下两个函数:

创建或替换函数 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

相关文章

  • 通过 Docker 启动的 .NET Core 解决方案无法找到本地 Microsoft SQL Server
    我遇到的问题是,当我使用Docker启动.NETCore项目时,无法找到在本地(而不是作为容器)运行的MicrosoftSQLServer。SwaggerUI的响应如下所示。TypeError:NetworkErrorwhenattemptingtofetchresource.这是我的连接。这是我的连接字符串......
  • mysql order by后跟case when
    在SQL中,ORDERBY子句用于对查询结果进行排序。当在ORDERBY后面使用CASE语句时,它的原理是:根据CASE语句中定义的条件和结果,为查询结果集中的每一行生成一个临时的排序值。然后,根据这些排序值对结果集进行排序。具体来说,CASE语句在ORDERBY中的工作原理如下:   条件判断:CASE......
  • Qt基于SQLite数据库的增删查改demo
    一、效果展示在Qt创建如图UI界面,主要包括“查询”、“添加”、“删除”、“更新”,四个功能模块。查询:从数据库中查找所有数据的所有内容,并显示在左边的QListWidget控件上。添加:在右边的QLineEdit标签上输入需要添加的内容,包含id,name,age三个字段,然后点击添加按钮,即可将数据......
  • oracle sqlplus 常用命令
    连接命令conn[ect]用法:conn用户名/密码@网络服务名[assysdba/sysoper]当用特权用户身份连接时,必须带上assysdba或是assysoperdisc[onnect]SQL>connscott/tigger已连接。SQL>disc从OracleDatabase10gEnterpriseEditionRelease10.2.0.1.0-Productio......
  • mysql阶段03 mysql多实例, 数据库主从, mysql5.6和5.7区别, 用户管理, 权限管理
    一、mysql的多实例nginx多实例,就是配置多个配置文件mysql多实例:1.有多个配置文件2.多端口3.多个socket文件4.多个日志文件5.多个server_id1.创建多实例存放目录之前数据库已安装在/usr/local/mysql下[root@db03~]#mkdir/usr/local/{3307,3308,3309}-p2.配置......
  • 图文详解Windows系统下搭建mysql开发环境——mysql Community 8 和 navicat Premium 1
    在正式开始学习使用MySQL之前,我们有必要先搭建一个良好的开发环境,让我们的学习和工作效率事半功倍。本文涉及到的软件百度云盘:链接:https://pan.baidu.com/s/1jj_YajEv8adeEjMrXLhOTQ?pwd=1023提取码:1023目录客户机—服务器软件MySQL版本MySQL的下载和安装MySQL服务的......
  • 宝塔搭建javaweb_宝塔工具+javaweb+mysql+tomcat部署项目
    1.首先我们得有一个安装了宝塔工具的云服务器首先我们要在服务器安全组开放宝塔常用的端口,看你是什么服务器,然后对应下面官网的教程就行了。腾讯云:https://www.bt.cn/bbs/thread-1229-1-1.html阿里云:https://www.bt.cn/bbs/thread-2897-1-1.html华为云:https://www.bt.cn/bbs/t......
  • 宝塔搭建javaweb_宝塔工具+javaweb+mysql+tomcat部署项目
    1.首先我们得有一个安装了宝塔工具的云服务器首先我们要在服务器安全组开放宝塔常用的端口,看你是什么服务器,然后对应下面官网的教程就行了。腾讯云:https://www.bt.cn/bbs/thread-1229-1-1.html阿里云:https://www.bt.cn/bbs/thread-2897-1-1.html华为云:https://www.bt.cn/bbs/t......
  • 入门级 SQL 注入实战!
    SQL注入原理可描述为通过用户可控参数中注入SQL语法,在程序员未对输入数据合法性做判断或过滤不严情况下,破坏原有SQL结构,达到编写程序时意料之外的结果。上篇文章已经搭建好了SQLi-labs靶场,一共包含65关,适用于GET和POST场景。接下来让我们开始SQL注入实战!一、实战准备安全测......
  • NoSuchModuleError: Can‘t load plugin: sqlalchemy.dialects:clickhouse解决方案
    NoSuchModuleError:Can'tloadplugin:sqlalchemy.dialects:clickhouse解决方案:全面解析问题概述当您使用SQLAlchemy连接ClickHouse数据库时,遇到NoSuchModuleError:Can'tloadplugin:sqlalchemy.dialects:clickhouse错误时,这意味着无法加载ClickHouse方言插件。......