2015-07-03 3 views
1

У меня появилось странное поведение комбинированных генерации_серии. В 2-х различных многоугольников, которые я пытался заполнить с сеткой, одна сетка была намного реже:Почему PostgreSQL сочетает серию неправильно?

enter image description here

Запрос был таким:

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, создайте правильное количество значений: 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

ответ

6

Это причуда, как несколько возвращающих набор функций выполняются при вызове в SELECT -list. Вы ожидаете, что результат станет перекрестным продуктом этих двух, но это не так, как это работает. На самом деле это самый низкий общий кратный числа строк из двух.

См:

Сравнить:

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 запросов, где вы вызываете набор - возвратные функции впункт:

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, термин:

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) 
3

От postgresql documentation

«Примечание: Ключ проблема с использованием функций set-returns в списке выбора, а не в предложении FROM, заключается в том, что включение нескольких функций возврата в один список выбора не ведет себя очень разумно. (Что вы на самом деле получить, если вы сделаете это является количество выходных строк равно наименьшему общему кратному чисел строк, полученных каждой из функций, возвращающих множества.)

+2

Однако LATERAL был введен только в 9.2, поэтому @ culebrón не повезло, пока они не могут обновиться. –

+0

Правильно. Однако в этом случае использование бокового ключевого слова вообще не требуется. «Этот (LATERAL) позволяет им ссылаться на столбцы, предоставленные предыдущими элементами FROM». И в нашем случае ссылки на предыдущие из пунктов вызова функции отсутствуют. А также в качестве примечания: «для функций ключевое слово (LATERAL) необязательно, аргументы функции могут содержать ссылки на столбцы, предоставленные предыдущими элементами FROM, в любом случае" –

1

Я использую этот формат:

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

Работает очень хорошо.

Смежные вопросы