【问题标题】:How to rewrite a SELECT ... CROSS JOIN LATERAL ... statement for older PostgreSQL versions?如何为旧 PostgreSQL 版本重写 SELECT ... CROSS JOIN LATERAL ... 语句?
【发布时间】:2015-05-28 16:51:07
【问题描述】:

我遇到了类似这样的查询:

SELECT *
FROM invoicable_interval i,
  LATERAL partition_into_months(i.start_date, i.stop_or_current_date) p;

... partition_into_months 的定义类似于:

CREATE FUNCTION partition_into_months(start_date DATE, stop_date DATE)
  RETURNS TABLE (start_date DATE, stop_date DATE, days INT)
AS $$ ... $$
LANGUAGE sql
IMMUTABLE;

所以我正在对辅助表进行可变间隔的交叉连接,因此使用(冗余)关键字 LATERAL。

这适用于 PostgreSQL 9.3.6 和 9.4.2,但是,对于 9.1.3,我收到以下错误消息:

[42601] ERROR: syntax error at or near "."
  Position: 398

指示的位置是表达式“i.start_date”中的点。

如何重写此 SQL 查询以将其移植回 PostgreSQL 9.1.3?

【问题讨论】:

  • 在我看来,该函数可以被(聚合)视图和/或 generate_series() 替换。显然,取决于函数的主体。

标签: sql postgresql postgresql-9.3 lateral


【解决方案1】:

PostgreSQL 支持在 SELECT 子句中调用 set-returning 函数。既然我们有 LATERAL,这在某种程度上已被弃用,当然也不鼓励这样做,因为它的行为相当不稳定,但它仍然有用。

在你的情况下,你可以写:

SELECT 
  i.*,
  (partition_into_months(i.start_date, i.stop_or_current_date)).*
FROM invoicable_interval i;

但是,这可能会导致每个返回的列调用一次partition_into_months,因为(fn).* 基本上被宏扩展为(fn).col1, (fn).col2, ...。为避免这种情况,您可以将其包装在子查询中,例如

SELECT (x.i).*, (x.p).*
FROM
(
  SELECT 
    i,
    partition_into_months(i.start_date, i.stop_or_current_date) p
  FROM invoicable_interval i
) x(i,p);

请注意,如果SELECT 列表中存在多个集合返回函数,则会遇到奇怪的结果。这不是您所期望的交叉连接。例如,比较:

SELECT generate_series(1,4), generate_series(1,4)

SELECT generate_series(1,4), generate_series(1,3);

【讨论】:

  • 第二个例子看起来很有趣,但我得到 '[42P01] ERROR: missing FROM-clause entry for table "i" Position: 8'
  • 找到了解决方法:您需要编写“SELECT (x.i).*, (x.p).* FROM ...”。您介意更新您的解决方案吗?那我就挑吧。
猜你喜欢
  • 2021-01-20
  • 2015-05-18
  • 2017-10-28
  • 2020-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
相关资源
最近更新 更多