【问题标题】:Are these two PostgreSQL functions equivalent? (RETURNS TABLE and RETURNS SETOF)这两个 PostgreSQL 函数是否等效? (返回表和返回集)
【发布时间】:2015-06-04 16:27:01
【问题描述】:

我同时处理两个 PostgreSQL 安装:我的本地环境和真正的远程服务器。可悲的是,服务器有一个旧版本(8.3.11),我的本地环境更新(9.4)。

我目前没有办法更新远程服务器,所以我正在将一个在 9.4 中运行良好的函数(它使用 RETURNS TABLE)转换为一个在 8.3.11 中应该可以正常运行的函数(它应该使用RETURNS SETOF)。

但是,虽然本地环境功能运行良好并且结果良好,但远程环境功能始终没有产生任何结果(使用完全相同的表!)

那么,这两个是完全等价的吗?

本地环境的新功能:

CREATE OR REPLACE FUNCTION pra2.GetGamesOnDate(date) 
  RETURNS TABLE (game_date date, is_home varchar, is_away varchar) AS $$
BEGIN
   RETURN QUERY  
   SELECT g.game_date, p1.team_name AS plays_at_home, p2.team_name AS plays_away
   FROM pra2.game g
   JOIN pra2.team p1 ON g.is_home = p1.team_id
   JOIN pra2.team p2 ON g.is_away = p2.team_id
   WHERE g.game_date = $1;
   IF NOT FOUND THEN
      RAISE EXCEPTION 'No hay partidos para la fecha %.', $1;
   END IF;
   RETURN;
END
$$
   LANGUAGE plpgsql;

这里是我修改后使用SETOF的函数

CREATE TYPE return_type AS 
(game_date date,
is_home varchar,
is_away varchar);

CREATE OR REPLACE FUNCTION pra2.GetGamesOnDate(date) 
  RETURNS SETOF return_type AS $$ 
DECLARE
   _rec return_type;
BEGIN
   RETURN QUERY  
   SELECT g.game_date, p1.team_name AS plays_at_home, p2.team_name AS plays_away
   FROM pra2.game g
   JOIN pra2.team p1 ON g.is_home = p1.team_id
   JOIN pra2.team p2 ON g.is_away = p2.team_id
   WHERE g.game_date = $1;
   IF NOT FOUND THEN
   RAISE EXCEPTION 'No hay partidos para la fecha %.', $1;
   END IF;
      RETURN next _rec;
END
$$
   LANGUAGE plpgsql;

它根本没有给出错误消息,它运行正常,但它只是没有产生任何结果(它总是引发异常消息),所以我想知道SETOF 查询中是否有错误设置...

【问题讨论】:

  • 您忘记添加您提到的(逐字)错误消息。应该一直在那里。

标签: sql postgresql plpgsql postgresql-8.3


【解决方案1】:

从文档来看,RETURN QUERY 在 PostgreSQL 8.3 中没有设置FOUND。 (PostgreSQL 9.1 的相关文档位于http://www.postgresql.org/docs/9.1/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-DIAGNOSTICS;相应的语句未出现在http://www.postgresql.org/docs/8.3/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-DIAGNOSTICS 的相应 PostgreSQL 8.3 文档中。)因此您的 IF NOT FOUND 检查没有做您想要的。

老实说,我不确定在 PostgreSQL 8.3 中实现这一目标的最佳方法是什么。一种选择是这样写:

CREATE OR REPLACE FUNCTION pra2.GetGamesOnDate(date) 
RETURNS SETOF return_type AS $$ 
DECLARE _rec return_type;
        has_rec boolean;
BEGIN
    has_rec := false;
    FOR _rec IN
        SELECT g.game_date, p1.team_name AS plays_at_home, p2.team_name AS plays_away
        FROM pra2.game g
        JOIN pra2.team p1 ON g.is_home = p1.team_id
        JOIN pra2.team p2 ON g.is_away = p2.team_id
        WHERE g.game_date = $1
    LOOP
        has_rec := true;
        RETURN NEXT _rec;
    END LOOP;
    IF NOT has_rec THEN
        RAISE EXCEPTION 'No hay partidos para la fecha %.', $1;
    END IF;
END
$$ LANGUAGE plpgsql;

(免责声明:未经测试。)

【讨论】:

  • 大师!它完全像你说的那样工作!我仍然会抱怨他们使用 8.3 的事实……但至少你给了我很大的推动力。我最近才开始学习postgreSQL,而且是通过阅读较新版本的文档来完成的,所以这有点混乱。
【解决方案2】:

PL/pgSQL does not set the special variable FOUND for RETURN QUERY in PostgreSQL 8.3,但是。那是added with Postgres 8.4

但您仍然不必求助于更复杂和昂贵的循环。您可以使用其他方法GET DIAGNOSTICS _ct = ROW_COUNT;,在手册中FOUND 旁边有说明:

CREATE TYPE return_type AS ( ...);

CREATE OR REPLACE FUNCTION pra2.GetGamesOnDate(date) 
  RETURNS SETOF return_type AS
$func$ 
DECLARE
   _ct int;
BEGIN
   RETURN QUERY  
   SELECT g.game_date, p1.team_name, p2.team_name
   FROM   pra2.game g
   JOIN   pra2.team p1 ON g.is_home = p1.team_id
   JOIN   pra2.team p2 ON g.is_away = p2.team_id
   WHERE  g.game_date = $1;

   GET DIAGNOSTICS _ct = ROW_COUNT;  -- number of returned rows.

   IF _ct = 0 THEN
      RAISE EXCEPTION 'No hay partidos para la fecha %.', $1;
   END IF;
END
$func$  LANGUAGE plpgsql;

另外,您的变量 _rec return_typeRETURN next _rec; 在原始版本中没有任何用途。

现在函数是等价的。你甚至可以在 Postgres 9.4 中使用它。

密切相关的答案:

另外:像 GetGamesOnDate 这样的驼峰式标识符在 Postgres 中是个坏主意。坚持使用合法的小写名称。

【讨论】:

  • 感谢您的替代版本,我会记住您的建议!
猜你喜欢
  • 2014-09-21
  • 2013-10-12
  • 2010-10-19
  • 1970-01-01
  • 2012-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-27
相关资源
最近更新 更多