¿Cómo preservar el orden original de los elementos en una matriz sin anotar?

19

Dada la cadena:

'Creo que PostgreSQL es ingenioso'

Me gustaría operar en las palabras individuales que se encuentran dentro de esa cadena. Esencialmente, tengo un separador del cual puedo obtener detalles de palabras y me gustaría unirme a una matriz no verificada de esa cadena en este diccionario.

Hasta ahora tengo:

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;

Esto logra los fundamentos de lo que esperaba hacer, pero no conserva el orden original de las palabras.

Pregunta relacionada:
PostgreSQL unnest () con número de elemento

swasheck
fuente
¿Desea procesar una cadena o una tabla completa de cadenas ? Si es así, ¿la tabla tiene una clave primaria?
Erwin Brandstetter
@ErwinBrandstetter una cadena en una tabla (que tiene una clave primaria)
swasheck

Respuestas:

24

WITH ORDINALITY en Postgres 9.4 o posterior

La nueva característica simplifica esta clase de problemas. La consulta anterior ahora puede ser simplemente:

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

O, aplicado a una tabla:

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

Detalles:

Sobre la LATERALunión implícita :

Postgres 9.3 o anterior - y una explicación más general

Para una sola cadena

Puede aplicar la función de ventana row_number()para recordar el orden de los elementos. Sin embargo, con lo habitual row_number() OVER (ORDER BY col)se obtienen números según el orden de clasificación , no la posición original en la cadena.

Simplemente puede omitir ORDER BYpara obtener la posición "tal cual":

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

Rendimiento de regexp_split_to_table()degrada con cuerdas largas. unnest(string_to_array(...))escala mejor:

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

Sin embargo, aunque esto normalmente funciona y nunca lo he visto romperse en consultas simples, Postgres no afirma nada en cuanto al orden de las filas sin un explícito ORDER BY.

Para garantizar números ordinales de elementos en la cadena original, use generate_subscript()(mejorado con comentarios de @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;

Para una mesa de cuerdas

Agregar PARTITION BY ida la OVERcláusula ...

Tabla de demostración:

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

Lo uso ctidcomo sustituto ad-hoc para una clave primaria . Si tiene una (o una columna única ), úsela.

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

Esto funciona sin ninguna identificación distinta:

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.

Respuesta a la pregunta

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
fuente
1
También puede explotar peculiar comportamiento SRF-en-SELECT lista de Pg: SELECT generate_series(1,array_length(word_array,1)), unnest(word_array) FROM ..... 9.3 LATERALpodría proporcionar mejores soluciones a este problema.
Craig Ringer
2
¿No generate_subscripts(arr, 1)funcionaría en lugar de generate_series(1, array_upper(arr, 1))? Prefiero lo primero por claridad.
dezso
1
@ Erwin, ¿has visto esta publicación CON ORDINALIDAD de depesz?
Jack Douglas
1
@JackDouglas: El viernes tuvimos una discusión sobre un tema relacionado , lo que me llevó a un descubrimiento similar. Agregué un poco a la respuesta.
Erwin Brandstetter
1
El enlace para "detalles" solo enlaza con esta misma página. Eso es confuso
Comodín el