Jak zachować pierwotną kolejność elementów w nie odkrytej tablicy?

19

Biorąc pod uwagę ciąg:

„Myślę, że PostgreSQL jest fajny”

Chciałbym operować poszczególnymi słowami znajdującymi się w tym ciągu. Zasadniczo mam osobny element, z którego mogę uzyskać szczegółowe informacje na temat słów i chciałbym dołączyć do nie odkrytej tablicy tego łańcucha w tym słowniku.

Do tej pory mam:

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;

Osiąga to podstawy tego, co miałem nadzieję zrobić, ale nie zachowuje oryginalnej kolejności słów.

Powiązane pytanie:
PostgreSQL unnest () z numerem elementu

swasheck
źródło
Czy chcesz przetworzyć jeden ciąg lub całą tabelę ciągów ? Jeśli tak, to czy tabela ma klucz podstawowy?
Erwin Brandstetter,
@ErwinBrandstetter jeden ciąg w tabeli (który ma klucz podstawowy)
swasheck

Odpowiedzi:

24

WITH ORDINALITY w Postgres 9.4 lub nowszym

Nowa funkcja upraszcza tę klasę problemów. Powyższe zapytanie może teraz być po prostu:

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

Lub zastosowane do tabeli:

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

Detale:

Informacje o niejawnym LATERALpołączeniu:

Postgres 9.3 lub starszy - i bardziej ogólne wyjaśnienie

Dla pojedynczego ciągu

Możesz zastosować funkcję okna, row_number()aby zapamiętać kolejność elementów. Jednak w zwykłym przypadku row_number() OVER (ORDER BY col)otrzymujesz liczby zgodnie z kolejnością sortowania , a nie pierwotną pozycję w ciągu.

Możesz po prostu pominąć, ORDER BYaby uzyskać pozycję „jak jest”:

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

Wydajność regexp_split_to_table()degradacji z długimi łańcuchami. unnest(string_to_array(...))skaluje się lepiej:

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

Jednak, chociaż normalnie działa to i nigdy nie widziałem, aby łamał się w prostych zapytaniach, Postgres nie zapewnia niczego o kolejności wierszy bez wyraźnego ORDER BY.

Aby zagwarantować liczbę porządkową elementów w oryginalnym łańcuchu, użyj generate_subscript()(poprawione komentarzem @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;

Dla tabeli ciągów znaków

Dodaj PARTITION BY iddo OVERklauzuli ...

Tabela demo:

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

Używam ctidjako ad-hoc zamiennika klucza podstawowego . Jeśli masz jedną (lub dowolną unikalną kolumnę ), użyj jej zamiast tego.

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

To działa bez wyraźnego ID:

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.

Odpowiedz na pytanie

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;
Erwin Brandstetter
źródło
1
Można również wykorzystać ekscentryczne zachowanie PG SRF-in-select-listy: SELECT generate_series(1,array_length(word_array,1)), unnest(word_array) FROM ..... 9.3 LATERALmoże zapewnić lepsze rozwiązania tego problemu.
Craig Ringer
2
Nie generate_subscripts(arr, 1)działałby zamiast generate_series(1, array_upper(arr, 1))? Wolę tę pierwszą dla jasności.
dezso,
1
@Erwin, czy widziałeś ten post Z ORDINALITYM z depesz?
Jack Douglas,
1
@JackDouglas: Tak się składa, że w piątek rozmawialiśmy na podobny temat , co doprowadziło mnie do podobnego odkrycia. Dodałem trochę do odpowiedzi.
Erwin Brandstetter
1
Link do „szczegółów” po prostu prowadzi do tej samej strony. To mylące.
Wildcard,