【问题标题】:Calling an insert *function* from a CTE in a SELECT query in Postgres 13.4在 Postgres 13.4 的 SELECT 查询中从 CTE 调用 insert *function*
【发布时间】:2021-12-22 16:18:09
【问题描述】:

我正在编写实用程序代码来运行pg_cron,有时希望例程将一些结果插入到位于dba.event_log 的自定义表中。我有一个基本的日志表作为起点:

DROP TABLE IF EXISTS dba.event_log;

CREATE TABLE IF NOT EXISTS dba.event_log (
    dts      timestamp    NOT NULL DEFAULT now(),
    name     citext       NOT NULL DEFAULT '',
    details  citext       NOT NULL DEFAULT '');

下面的玩具示例执行select 操作,然后将该值用作外部查询的结果,并作为insertvalues 元素进入event_log

WITH

values_cte AS (
 select clock_timestamp() as ct
),

log as(
insert into event_log (
               name,
               details)

     values (
             'CTE INSERT check',
             'clock = ' || (select ct::text from values_cte)
             )
)

select * from values_cte;

select * from event_log;

每次运行此程序时,我都会收到一个新的日志条目,其中带有 clock_timestamp(),以便轻松查看正在发生的事情:

+----------------------------+------------------+---------------------------------------+
| dts                        | name             | details                               |
+----------------------------+------------------+---------------------------------------+
| 2021-11-10 11:58:43.919151 | CTE INSERT check | clock = 2021-11-10 11:58:43.919821+11 |
| 2021-11-10 11:58:56.769512 | CTE INSERT check | clock = 2021-11-10 11:58:56.769903+11 |
| 2021-11-10 11:58:59.632619 | CTE INSERT check | clock = 2021-11-10 11:58:59.632822+11 |
| 2021-11-10 12:00:50.442282 | CTE INSERT check | clock = 2021-11-10 12:00:50.442646+11 |
+----------------------------+------------------+---------------------------------------+

稍后我可能会丰富表格,现在我将日志inserts 变成一个简单的调用。下面是一个简单的insert函数:

DROP FUNCTION IF EXISTS dba.event_log_add(citext,citext);

CREATE FUNCTION dba.event_log_add(
   name_in        citext,
   description_in citext)

RETURNS int4

LANGUAGE sql AS

$BODY$

insert into event_log (name, details)
     values (name_in, description_in)
   returning 1;

$BODY$;

看到我应该能够重写原始查询来调用函数,像这样:

WITH

values_cte AS (
 select clock_timestamp() as ct
),

log as (
select * from dba.event_log_add(   
             'CTE event_log_add check',
             'clock = ' || (select ct::text from values_cte)
             )               
)

select * from values_cte;

这里唯一的区别是VALUES 现在作为参数传递给dba.event_log_add,而不是直接在查询中用于INSERT。我收到此错误:

ERROR:  function dba.event_log_add(unknown, text) does not exist
LINE 8: select * from dba.event_log_add(   
                      ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts. 0.000 seconds. (Line 1).

我试过了

  • 显式转换
  • 将函数重写为存储过程并使用CALL
  • 重写PL/PgSQL中的函数,返回VOID,运行PERFORM

似乎没有任何效果。我检查了search_path,使用合格 名称,检查权限等。有些方法会抛出似乎不适用的错误, 像上面的一样,其他的没有错误,也没有插入数据。直接运行,功能正常,只是在CTE内炸了。

我想我在使用函数而不是直接INSERT 方面遗漏了一些东西。有没有好的方法来做到这一点?在查看文档并在此处搜索更多信息后,我对规则更清楚了bit。但也不完全。如果我没看错,数据修改 CTE 由外部查询规则/调节。肯定有一些我没有掌握的微妙之处。我是否以某种方式更改上下文以将 INSERT 移动到函数中,从而解释查询和 CTE 中的代码?

https://www.postgresql.org/docs/13/queries-with.html#QUERIES-WITH-MODIFYING

【问题讨论】:

    标签: postgresql sql-insert common-table-expression stored-functions


    【解决方案1】:

    您的函数需要citext 类型的参数,但您正在传递text 类型的值。您需要转换参数:

    WITH values_cte AS (
     select clock_timestamp() as ct
    ),log as (
      select event_log_add('CTE event_log_add check'::citext,
                           ('clock = ' || (select ct::text from values_cte))::citext)               
    )
    select * 
    from log;
    

    将参数定义为text 可能更容易,在 INSERT 期间,转换将自动完成:

    CREATE FUNCTION event_log_add(
       name_in        text,
       description_in text)
    RETURNS int4
    LANGUAGE sql AS
    $BODY$
      insert into event_log (name, details)
      values (name_in, description_in)
      returning 1;
    $BODY$;
    
    WITH values_cte AS (
     select clock_timestamp() as ct
    ),log as (
      select event_log_add('CTE event_log_add check',
                           'clock = ' || (select ct::text from values_cte))   
    )
    select * 
    from log;
    

    如果需要,您可以在函数内添加显式强制转换。

    【讨论】:

    • 我试过在通话中进行投射,但似乎没有用。您的答案确实有效,而且看起来更整洁。谢谢
    猜你喜欢
    • 1970-01-01
    • 2022-10-15
    • 1970-01-01
    • 1970-01-01
    • 2021-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多