PL/pgSQL:触发函数返回不正确的数据

Posted

技术标签:

【中文标题】PL/pgSQL:触发函数返回不正确的数据【英文标题】:PL/pgSQL: Trigger Function returning incorrect data 【发布时间】:2021-12-08 09:14:56 【问题描述】:

我有一个带有参数“select_date”和“sponsor”的函数,它应该根据作为参数传递的值返回一些数据表:

SET search_path to Olympic;

CREATE OR REPLACE FUNCTION fn_get_info_by_sponsor
       (register_date tb_register.register_ts%type, sponsor_name tb_finance.sponsor_name%type)
RETURNS SETOF sponsor_data LANGUAGE plpgsql AS $$
DECLARE
      sp_data sponsor_data ;
BEGIN
      SELECT 
                 tb_sponsor.email,
                 tb_finance.sponsor_name,
                 tb_athlete.name,
                 tb_discipline.name,
                 tb_register.round_number,  
                 tb_register.register_measure, 
                 tb_register.register_position,
                 DATE(tb_register.register_ts)
                
       INTO sp_data
       FROM olympic.tb_sponsor
      INNER JOIN olympic.tb_finance
           ON (tb_finance.sponsor_name = tb_sponsor.name)
      INNER JOIN olympic.tb_athlete
           ON tb_athlete.athlete_id = tb_finance.athlete_id
      INNER JOIN olympic.tb_register
           ON tb_register.athlete_id = tb_athlete.athlete_id
      INNER JOIN olympic.tb_discipline
           ON tb_discipline.discipline_id = tb_register.discipline_id
      ORDER BY tb_sponsor.email ;

      RETURN NEXT sp_data ;
END ;  
$$;

该函数部分工作,但它返回不正确的值。当我使用参数 '2021-06-02 00:00:00', 'Adidas' 其中 'Adidas' 是赞助商时,它会将赞助商名称返回为 VISA:

SELECT * FROM fn_get_info_by_sponsor('2021-06-02 00:00:00', 'Adidas')

sponsor email, sponsor_name, athlete_name, discipline_name, round_number, register_measure, register_position, register_ts,
  NULL  "VISA"  "JACOBY Lydia"  "Swimming"  1   NULL    3   "2021-06-07"

谁能看出我做错了什么?

谢谢!

CREATE TYPE sponsor_data AS (
email email_type,
sponsor_name CHARACTER VARYING(100),
athletes_name CHARACTER VARYING(50),
discilpine_name CHARACTER VARYING(50),
round_number  INT ,
mark CHARACTER(12),
register_position INT,
register_ts  DATE
);

【问题讨论】:

好吧,您不要使用 WHERE 子句中的参数来限制结果。而且您也只返回查询的第一行。 @a_horse_with_no_name SETOF 语句是否应该返回行集? 不,“return”语句从函数返回一些东西。您正在使用您的 return 语句返回一行。 你运行return next 一次,所以你只返回一行。 RETURN SETOF 是函数定义的一部分。它不返回任何东西。 return next 每次运行时返回一行。您的代码只运行return next一次,因此您的函数将永远不会返回超过一行。 【参考方案1】:

对于返回 setof 的 PL/pgSQL 函数,您必须使用 RETURN QUERY 子句,请参阅 manual :

CREATE OR REPLACE FUNCTION fn_get_info_by_sponsor
       (register_date tb_register.register_ts%type, sponsor_name tb_finance.sponsor_name%type)
RETURNS SETOF sponsor_data LANGUAGE plpgsql AS $$
BEGIN
      RETURN QUERY
      SELECT row(
                 tb_sponsor.email,
                 tb_finance.sponsor_name,
                 tb_athlete.name,
                 tb_discipline.name,
                 tb_register.round_number,  
                 tb_register.register_measure, 
                 tb_register.register_position,
                 DATE(tb_register.register_ts)
                ) :: sponsor_data 
       FROM olympic.tb_sponsor
      INNER JOIN olympic.tb_finance
           ON (tb_finance.sponsor_name = tb_sponsor.name)
      INNER JOIN olympic.tb_athlete
           ON tb_athlete.athlete_id = tb_finance.athlete_id
      INNER JOIN olympic.tb_register
           ON tb_register.athlete_id = tb_athlete.athlete_id
      INNER JOIN olympic.tb_discipline
           ON tb_discipline.discipline_id = tb_register.discipline_id
      ORDER BY tb_sponsor.email ;
END ;  
$$;

或者您可以简单地编写一个 SQL 用户定义函数:

CREATE OR REPLACE FUNCTION fn_get_info_by_sponsor
       (register_date tb_register.register_ts%type, sponsor_name tb_finance.sponsor_name%type)
RETURNS SETOF sponsor_data LANGUAGE sql AS $$
      SELECT row(
                 tb_sponsor.email,
                 tb_finance.sponsor_name,
                 tb_athlete.name,
                 tb_discipline.name,
                 tb_register.round_number,  
                 tb_register.register_measure, 
                 tb_register.register_position,
                 DATE(tb_register.register_ts)
                ) :: sponsor_data 
       FROM olympic.tb_sponsor
      INNER JOIN olympic.tb_finance
           ON (tb_finance.sponsor_name = tb_sponsor.name)
      INNER JOIN olympic.tb_athlete
           ON tb_athlete.athlete_id = tb_finance.athlete_id
      INNER JOIN olympic.tb_register
           ON tb_register.athlete_id = tb_athlete.athlete_id
      INNER JOIN olympic.tb_discipline
           ON tb_discipline.discipline_id = tb_register.discipline_id
      ORDER BY tb_sponsor.email ;
$$;

【讨论】:

谢谢(:返回查询没有解决我的问题。我收到:错误:无法打开 SELECT 查询作为光标。 您的问题不在函数fn_get_info_by_sponsor 内,而在于您在CURSOR 内调用此函数的方式!请显示调用函数fn_get_info_by_sponsor的函数代码。 这就是我调用函数的方式:SELECT * FROM fn_get_info_by_sponsor('2021-06-02 00:00:00', 'Adidas') 我已经更新了我的答案,但我不确定它是否能解决您的错误。您能否创建表格定义并在dbfiddle 中插入数据样本,以便我们分析您的问题?

以上是关于PL/pgSQL:触发函数返回不正确的数据的主要内容,如果未能解决你的问题,请参考以下文章

重构 PL/pgSQL 函数以返回各种 SELECT 查询的输出

用于行 mod 3 的 PL/pgSQL 游标

在 PL/pgSQL 函数中使用变量

如何在 pl/pgsql 中创建返回 refcursor 和 totalRow 的函数/过程?

如何在 PL/pgSQL 中按行类型返回表

简单的 PL/pgSQL 函数的错误