8

我正在尝试在左外连接中调用存储过程传递参数,如下所示:

select i.name,sp.*
from items i
left join compute_prices(i.id,current_date) as sp(price numeric(15,2), 
          discount numeric(5,2), taxes numeric(5,2)) on 1=1
where i.type = 404;

compute_prices()返回一组记录。
这是 postgres 显示的消息:

错误:对表“i”的 FROM 子句条目的引用无效

...左加入 compute_prices( i.id ,current_date)...

提示:表“i”有一个条目,但不能从这部分查询中引用它。

这种查询在 Firebird 中有效。有没有一种方法可以通过仅使用查询来使其工作?我不想创建另一个循环遍历项目并单独调用compute_prices().

4

3 回答 3

6

通常,您可以使用@Daniel 提供的简单语法扩展众所周知的行类型(也称为记录类型、复杂类型、复合类型):

SELECT i.name, (compute_prices(i.id, current_date)).*
FROM   items i
WHERE  i.type = 404;

但是,如果你的描述是准确的......

compute_prices sp 返回一组记录。

...我们正在处理匿名记录。Postgres 不知道如何扩展匿名记录,绝望地抛出一个 EXCEPTION:

ERROR:  a column definition list is required for functions returning "record"

PostgreSQL 9.3

Postgres 9.3 中有一个解决方案。LATERAL,正如@a_horse 在评论中提到的那样:

SELECT i.name, sp.*
FROM   items i
LEFT   JOIN LATERAL compute_prices(i.id,current_date) AS sp (
                       price    numeric(15,2)
                      ,discount numeric(5,2)
                      ,taxes    numeric(5,2)
                      ) ON TRUE
WHERE i.type = 404;

手册中的详细信息。

PostgreSQL 9.2 及更早版本

事情变得多毛。这是一种解决方法:编写一个包装函数,将您的匿名记录转换为众所周知的类型:

CREATE OR REPLACE FUNCTION compute_prices_wrapper(int, date)
  RETURNS TABLE (
            price    numeric(15,2)
           ,discount numeric(5,2)
           ,taxes    numeric(5,2)
          ) AS
$func$
    SELECT * FROM compute_prices($1, $2)
    AS t(price    numeric(15,2)
        ,discount numeric(5,2)
        ,taxes    numeric(5,2));
$func$ LANGUAGE sql;

然后您可以使用@Daniel 的简单解决方案,只需放入包装函数:

SELECT i.name, (compute_prices_wrapper(i.id, current_date)).*
FROM   items i
WHERE  i.type = 404;

PostgreSQL 8.3 及更早版本

PostgreSQL 8.3 刚刚达到 EOL,截至目前(2013 年 2 月)不受支持
因此,如果可能,您最好升级。但如果你不能:

CREATE OR REPLACE FUNCTION compute_prices_wrapper(int, date
           ,OUT price    numeric(15,2)
           ,OUT discount numeric(5,2)
           ,OUT taxes    numeric(5,2))
  RETURNS SETOF record AS
$func$
    SELECT * FROM compute_prices($1, $2)
    AS t(price    numeric(15,2)
        ,discount numeric(5,2)
        ,taxes    numeric(5,2));
$func$ LANGUAGE sql;

也可以在以后的版本中使用。

正确的解决方案是修复您的函数compute_prices()以返回一个众所周知的类型。返回的函数SETOF record通常是 PITA。我只戳那些用五米长的杆子。

于 2013-02-08T18:53:21.663 回答
3

假设compute_prices函数总是返回一个包含 3 个价格的记录,你可以将它的返回类型设置为TABLE (price numeric(15,2), discount numeric(5,2),taxes numeric(5,2)),然后我相信你想要的可以表示为:

SELECT i.name, (compute_prices(i.id,current_date)).*
  FROM items i
WHERE i.type=404;

请注意,在我看来,LEFT JOIN ON 1=1它与无约束的普通 JOIN(或 CROSS JOIN)没有什么不同,我将这个问题解释为实际上与左连接无关。

于 2013-02-08T14:10:48.140 回答
1

我相信丹尼尔的答案也会起作用,但还没有尝试过。我确实知道我在名为 logging 的模式中有一个名为 list_failed_jobs2 的 SP,还有一个名为 Dual 的虚拟表(如在 Oracle 中),以下语句适用于我:

select * from Dual left join 
              (select * from logging.list_failed_jobs2()) q on 1=1;

请注意,如果没有括号、相关性 (q) 或 ON 子句,SP 调用将无法工作。我的 SP 也返回一个 SETOF。

因此,我怀疑这样的事情会为你工作:

select i.name,sp.*
from items i
left join (select * from compute_prices(i.id,current_date)) as sp on 1=1
where i.type = 404;

希望有帮助。

于 2013-02-08T18:00:27.960 回答