Параллельный unnest() и порядок сортировки в PostgreSQL
Я понимаю, что с помощью
SELECT unnest(ARRAY[5,3,9]) as id
без ORDER BY предложение, порядок результирующего набора не гарантируется. Я мог бы, например, получить:
id
--
3
5
9
но как насчет следующего запроса:
SELECT
unnest(ARRAY[5,3,9]) as id,
unnest(ARRAY(select generate_series(1, array_length(ARRAY[5,3,9], 1)))) as idx
ORDER BY idx ASC
гарантируется ли, что 2 unnest() вызовы (которые имеют одинаковую длину) будут разворачиваться параллельно и что индекс idx действительно будет соответствовать положению элемента в массиве?
Я использую PostgreSQL 9.3.3.
2 ответов
Да, это особенность Postgres и параллельная неустановка гарантированный для синхронизации (если все массивы имеют одинаковое количество элементов).
Postgres 9.4 добавляет чистое решение для параллельного unnest:
порядок результирующих строк не гарантируется. На самом деле, с таким простым утверждением as:
SELECT unnest(ARRAY[5,3,9]) AS id
результирующий порядок строк "гарантирован", но Postgres ничего не утверждает. Оптимизатор запросов может упорядочивать строки по своему усмотрению, если порядок явно не определен. Это может иметь побочные эффекты в более сложных запросов.
если второй запрос в вашем вопросе-это то, что вы на самом деле хотите (добавьте номер индекса к элементам unnested array), есть лучший способ с generate_subscripts ():
SELECT unnest(ARRAY[5,3,9]) AS id
, generate_subscripts(ARRAY[5,3,9], 1) AS idx
ORDER BY idx;
подробности в этом соответствующем ответе:
вам будет интересно WITH ORDINALITY в Postgres 9.4:
затем вы можете использовать:
SELECT * FROM unnest(ARRAY[5,3,9]) WITH ORDINALITY tbl(id, idx);
короткий ответ:: нет, idx не будет соответствовать позициям массива, при принятии предпосылки, что unnest() выход может быть случайным образом.
демо:
с момента текущей реализации unnest фактически выведите строки в порядке элементов, я предлагаю добавить слой поверх него, чтобы имитировать случайный порядок:
CREATE FUNCTION unnest_random(anyarray) RETURNS setof anyelement
language sql as
$$ select unnest() order by random() $$;
затем проверьте несколько исполнений вашего запроса с помощью unnest заменить unnest_random:
SELECT
unnest_random(ARRAY[5,3,9]) as id,
unnest_random(ARRAY(select generate_series(1, array_length(ARRAY[5,3,9], 1)))) as idx
ORDER BY idx ASC
пример вывода:
id | idx ----+----- 3 | 1 9 | 2 5 | 3
id=3 связан с idx=1 но 3 в 2-й позиции в массиве. Это все неправильно.
что не так в запросе: предполагается, что первый unnest перетасует элементы, используя ту же перестановку, что и вторая unnest (перестановка в математическом смысле: связь между порядком в массиве и порядком строк). Но это предположение противоречит предпосылке, что порядок вывода unnest непредсказуемо для начала.
об этом вопросе:
гарантируется ли, что 2 вызова unnest () (которые имеют то же самое длина) будет разворачиваться параллельно
на select unnest(...) X1, unnest(...) X2 С X1 и X2 тип SETOF something и имея такое же количество строк,X1 и X2 будет спарен в окончательном выходе так, что X1 значение на row N ждет X2 значение в той же строке N.
(это своего рода объединение для столбцов, в отличие от декартового произведения).
но я бы не сказала, что это спаривание, как разверните параллельно, так что я не уверен, что это то, что вы имели в виду.
в любом случае это сопряжение не помогает с проблемой, так как это происходит после того, как unnest вызовы потеряли позиции массива.
альтернатива: In этот нить из списка рассылки pgsql-sql предлагается следующая функция:
CREATE OR REPLACE FUNCTION unnest_with_ordinality(anyarray, OUT value
anyelement, OUT ordinality integer)
RETURNS SETOF record AS
$$
SELECT [i], i FROM
generate_series(array_lower(,1),
array_upper(,1)) i;
$$
LANGUAGE sql IMMUTABLE;
основываясь на этом, мы можем заказать второй выходной столбец:
select * from unnest_with_ordinality(array[5,3,9]) order by 2;
value | ordinality
-------+------------
5 | 1
3 | 2
9 | 3
С postgres 9.4 и выше: элемент WITH ORDINALITY предложение, которое может следовать за вызовами возвращающей функции SET, обеспечит эту функциональность общим способом.