Как сохранить исходный порядок элементов в неопубликованном массиве?

19

Учитывая строку:

«Я думаю, что PostgreSQL отличный»

Я хотел бы оперировать отдельными словами, найденными в этой строке. По сути, у меня есть отдельный раздел, из которого я могу получить информацию о слове, и я хотел бы присоединить к этому словарю необъявленный массив этой строки.

Пока что у меня есть:

select word, meaning, partofspeech
from unnest(string_to_array('I think that PostgreSQL is nifty',' ')) as word
from table t
join dictionary d
on t.word = d.wordname;

Это завершает основы того, что я надеялся сделать, но не сохраняет первоначальный порядок слов.

Смежный вопрос:
PostgreSQL unnest () с номером элемента

swasheck
источник
Вы хотите обработать одну строку или целую таблицу строк ? Если да, есть ли у таблицы первичный ключ?
Эрвин Брандштеттер
@ErwinBrandstetter одна строка в таблице (которая имеет первичный ключ)
swasheck

Ответы:

24

WITH ORDINALITY в Postgres 9.4 или позже

Новая функция упрощает этот класс проблем. Вышеупомянутый запрос теперь может быть просто:

SELECT *
FROM   regexp_split_to_table('I think Postgres is nifty', ' ') WITH ORDINALITY x(word, rn);

Или применительно к столу:

SELECT *
FROM   tbl t, regexp_split_to_table(t.my_column, ' ') WITH ORDINALITY x(word, rn);

Детали:

О неявном LATERALобъединении:

Postgres 9.3 или старше - и более общее объяснение

Для одной строки

Вы можете применить оконную функцию, row_number()чтобы запомнить порядок элементов. Однако, как обычно, row_number() OVER (ORDER BY col)вы получаете числа в соответствии с порядком сортировки , а не с исходной позицией в строке.

Вы можете просто пропустить, ORDER BYчтобы получить позицию «как есть»:

SELECT *, row_number() OVER () AS rn
FROM   regexp_split_to_table('I think Postgres is nifty', ' ') AS x(word);

Исполнение regexp_split_to_table()деградирует с длинными струнами. unnest(string_to_array(...))весы лучше:

SELECT *, row_number() OVER () AS rn
FROM   unnest(string_to_array('I think Postgres is nifty', ' ')) AS x(word);

Однако, хотя это обычно работает, и я никогда не видел, чтобы оно ломалось в простых запросах, Postgres ничего не утверждает относительно порядка строк без явного ORDER BY.

Чтобы гарантировать порядковые номера элементов в исходной строке, используйте generate_subscript()(улучшено с комментарием @deszo):

SELECT arr[rn] AS word, rn
FROM   (
   SELECT *, generate_subscripts(arr, 1) AS rn
   FROM   string_to_array('I think Postgres is nifty', ' ') AS x(arr)
   ) y;

Для таблицы строк

Добавить PARTITION BY idв OVERпункт ...

Демо-таблица:

CREATE TEMP TABLE strings(string text);
INSERT INTO strings VALUES
  ('I think Postgres is nifty')
 ,('And it keeps getting better');

Я использую в ctidкачестве специальной замены для первичного ключа . Если у вас есть один (или любой уникальный столбец ), используйте его вместо этого.

SELECT *, row_number() OVER (PARTITION BY ctid) AS rn
FROM  (
   SELECT ctid, unnest(string_to_array(string, ' ')) AS word
   FROM   strings
   ) x;

Это работает без какого-либо четкого идентификатора:

SELECT arr[rn] AS word, rn
FROM  (
   SELECT *, generate_subscripts(arr, 1) AS rn
   FROM  (
      SELECT string_to_array(string, ' ') AS arr
      FROM   strings
      ) x
   ) y;

SQL Fiddle.

Ответ на вопрос

SELECT z.arr, z.rn, z.word, d.meaning   -- , partofspeech -- ?
FROM  (
   SELECT *, arr[rn] AS word
   FROM  (
      SELECT *, generate_subscripts(arr, 1) AS rn
      FROM  (
         SELECT string_to_array(string, ' ') AS arr
         FROM   strings
         ) x
      ) y
   ) z
JOIN   dictionary d ON d.wordname = z.word
ORDER  BY z.arr, z.rn;
Эрвин Брандштеттер
источник
1
Вы можете также использовать изворотливое поведение Pg в SRF-в-список выбора: SELECT generate_series(1,array_length(word_array,1)), unnest(word_array) FROM ..... 9.3 LATERALмогут предложить более хорошие решения этой проблемы.
Крейг Рингер
2
Не будет generate_subscripts(arr, 1)работать вместо generate_series(1, array_upper(arr, 1))? Я бы предпочел первое для ясности.
Дезсо
1
@ Эрвин, ты видел это с сообщением ОБЫЧНОСТИ от depesz?
Джек Дуглас,
1
@JackDouglas: как оказалось, в пятницу у нас была дискуссия на смежную тему , которая привела меня к аналогичному открытию. Я добавил немного к ответу.
Эрвин Брандштеттер,
1
Ссылка для «деталей» просто ссылки на эту же страницу. Это сбивает с толку.
Wildcard