如何重写旧的PostgreSQL版本的SELECT ... CROSS JOIN LATERAL ...语句?

时间:2015-05-28 16:51:08

标签: sql postgresql postgresql-9.3 lateral

我面临类似的问题:

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?

1 个答案:

答案 0 :(得分:1)

PostgreSQL支持在SELECT子句中调用set-returns函数。由于我们有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);