为什么 PostgreSQL 以错误的方式组合系列?

Why does PostgreSQL combine series in a wrong way?

我得到了组合 generate_series 的一些奇怪行为。在我尝试用网格填充的 2 个不同的多边形中,一个网格要少得多:

查询是这样的:

SELECT
    osm_id ,
        generate_series(floor(st_xmin(way))::int, ceiling(st_xmax(way))::int, 150) x,
        generate_series(floor(st_ymin(way))::int, ceiling(st_ymax(way))::int, 150) y 
from osm_polygon
order by osm_id, x, y;

我试着追踪问题,只是输入了 min/max 坐标。从 min/max 值生成系列创建正确数量的值:分别为 9 行和 12 行。

  => select generate_series(9237195, 9238873, 150) x;
      x    
  ---------
   9237195
   9237345
   9237495
   9237645
   9237795
   9237945
   9238095
   9238245
   9238395
   9238545
   9238695
   9238845
  (12 rows)

  => select generate_series(7371701, 7372922, 150) y order by y;
      y    
  ---------
   7371701
   7371851
   7372001
   7372151
   7372301
   7372451
   7372601
   7372751
   7372901
  (9 rows)

加起来应该有 108 行,对吧?不,只有 36 行:

=> select generate_series(9237195, 9238873, 150) x, generate_series(7371701, 7372922, 150) y order by x, y;
      x    |    y    
  ---------+---------
   9237195 | 7371701
   9237195 | 7372151
   9237195 | 7372601
   9237345 | 7371851
   9237345 | 7372301
   9237345 | 7372751
   9237495 | 7372001
   9237495 | 7372451
   9237495 | 7372901
   9237645 | 7371701
   9237645 | 7372151
   9237645 | 7372601
   9237795 | 7371851
   9237795 | 7372301
   9237795 | 7372751
   9237945 | 7372001
   9237945 | 7372451
   9237945 | 7372901
   9238095 | 7371701
   9238095 | 7372151
   9238095 | 7372601
   9238245 | 7371851
   9238245 | 7372301
   9238245 | 7372751
   9238395 | 7372001
   9238395 | 7372451
   9238395 | 7372901
   9238545 | 7371701
   9238545 | 7372151
   9238545 | 7372601
   9238695 | 7371851
   9238695 | 7372301
   9238695 | 7372751
   9238845 | 7372001
   9238845 | 7372451
   9238845 | 7372901
(36 rows)

蛮力实际上在这里起作用:

with a as (select generate_series(9237195, 9238873, 150) x),
    b as (select generate_series(7371701, 7372922, 150) y)
select x, y from a, b;

生成 108 行。很好,除了查询变得更复杂。

为什么会这样?

Postgres 版本为 9.1

这是在 SELECT-list 中调用时如何执行多个集合返回函数的怪癖。您希望结果是两者的叉积,但事实并非如此。它实际上是两者行数的最小公倍数。

参见:

  • Why do these join differently based on size?

比较:

test=>     SELECT generate_series(1,3) aval, generate_series(1,4) bval;
 aval | bval 
------+------
    1 |    1
    2 |    2
    3 |    3
    1 |    4
    2 |    1
    3 |    2
    1 |    3
    2 |    4
    3 |    1
    1 |    2
    2 |    3
    3 |    4
(12 rows)

test=>     SELECT generate_series(1,3) aval, generate_series(1,3) bval;
 aval | bval 
------+------
    1 |    1
    2 |    2
    3 |    3
(3 rows)

因此,在 PostgreSQL 9.2 及更高版本上,您应该使用 LATERAL 查询,其中您在 FROM 子句中调用集合返回函数:

test=>     SELECT aval, bval FROM generate_series(1,3) aval CROSS JOIN LATERAL generate_series(1,3) bval;
 aval | bval 
------+------
    1 |    1
    1 |    2
    1 |    3
    2 |    1
    2 |    2
    2 |    3
    3 |    1
    3 |    2
    3 |    3
(9 rows)

test=>     SELECT aval, bval FROM generate_series(1,3) aval CROSS JOIN LATERAL generate_series(1,4) bval;
 aval | bval 
------+------
    1 |    1
    1 |    2
    1 |    3
    1 |    4
    2 |    1
    2 |    2
    2 |    3
    2 |    4
    3 |    1
    3 |    2
    3 |    3
    3 |    4
(12 rows)

在旧版本上,您可以使用 FROM 中的子查询来避免在 SELECT 项中有多个 SRF:

test=> SELECT generate_series(1,3) aval, bval FROM (SELECT generate_series(1,4)) AS x(bval);
 aval | bval 
------+------
    1 |    1
    2 |    1
    3 |    1
    1 |    2
    2 |    2
    3 |    2
    1 |    3
    2 |    3
    3 |    3
    1 |    4
    2 |    4
    3 |    4
(12 rows)

来自postgresql documentation

"注意:在 select 列表中使用集合返回函数而不是 FROM 子句的关键问题是将多个集合返回函数放在同一个 select list 的行为不太明智。(如果这样做,您实际得到的输出行数等于每个集合返回函数生成的行数的最小公倍数。)

我使用这种格式:

SELECT i,j FROM
generate_series(1,3) i, generate_series(1,3) j

效果很好。