Dada una cadena que puede contener varias instancias de un delimitador, quiero generar todas las subcadenas que comiencen después de ese carácter.
Por ejemplo, dada una cadena como 'a.b.c.d.e'
(o matriz {a,b,c,d,e}
, supongo), quiero generar una matriz como:
{a.b.c.d.e, b.c.d.e, c.d.e, d.e, e}
El uso previsto es como un disparador para llenar una columna para una consulta más fácil de las partes del nombre de dominio (es decir, buscar todo q.x.t.com
para consulta t.com
) siempre que se escriba en otra columna.
Parece una forma incómoda de resolver esto (y muy bien puede ser), pero ahora tengo curiosidad por saber cómo se podría escribir una función como esta en SQL (Postgres).
Estos son nombres de dominio de correo electrónico, por lo que es difícil decir cuál es el número máximo posible de elementos, pero ciertamente la gran mayoría sería <5.
fuente
Respuestas:
No creo que necesites una columna separada aquí; Este es un problema XY. Solo estás tratando de hacer una búsqueda de sufijos. Hay dos formas principales de optimizar eso.
Convierta la consulta de sufijo en una consulta de prefijo
Básicamente haces esto invirtiendo todo.
Primero cree un índice en el reverso de su columna:
Luego consulta usando lo mismo:
Puede
UPPER
enviar una llamada si desea que no distinga entre mayúsculas y minúsculas:Trigram Indexes
La otra opción son los índices trigram. Definitivamente debe usar esto si necesita consultas infijadas (
LIKE 'something%something'
oLIKE '%something%'
consultas de tipo).Primero habilite la extensión del índice de trigram:
(Esto debería venir con PostgreSQL listo para usar sin ninguna instalación adicional).
Luego cree un índice de trigrama en su columna:
Luego solo seleccione:
Una vez más, puede agregar un
UPPER
para que no distinga entre mayúsculas y minúsculas si lo desea:Su pregunta como esta escrita
Los índices Trigram realmente funcionan usando una forma algo más general de lo que estás pidiendo debajo del capó. Rompe la cadena en pedazos (trigramas) y construye un índice basado en ellos. El índice se puede usar para buscar coincidencias mucho más rápidamente que un análisis secuencial, pero para consultas de infijo y sufijo y prefijo. Siempre trate de evitar reinventar lo que alguien más ha desarrollado cuando pueda.
Créditos
Las dos soluciones son prácticamente textuales de Elegir un método de búsqueda de texto PostgreSQL . Recomiendo leerlo para un análisis detallado de las opciones de búsqueda de texto disponibles en PotsgreSQL.
fuente
Creo que este es mi favorito.
FILAS
Matrices
fuente
FILAS
O
Matrices
O
fuente
Pregunta formulada
Tabla de prueba:
CTE recursivo en una subconsulta LATERAL
El
CROSS JOIN LATERAL
(, LATERAL
para abreviar) es seguro, porque el resultado agregado de la subconsulta siempre devuelve una fila. Usted obtiene ...str = ''
en la tabla basestr IS NULL
en la tabla baseTerminado con un constructor de matriz barato en la subconsulta, por lo que no hay agregación en la consulta externa.
Una obra maestra de las características de SQL, pero la sobrecarga de rCTE puede evitar el máximo rendimiento.
Fuerza bruta para un número trivial de elementos.
Para su caso con un número trivialmente pequeño de elementos , un enfoque simple sin subconsulta puede ser más rápido:
Suponiendo un máximo de 5 elementos como usted comentó. Puede expandirse fácilmente para obtener más.
Si un dominio dado tiene menos elementos, las
substring()
expresiones en exceso devuelven NULL y son eliminadas porarray_remove()
.En realidad, la expresión de arriba (
right(str, strpos(str, '.')
), anidada varias veces puede ser más rápida (aunque difícil de leer) ya que las funciones de expresión regular son más caras.Una bifurcación de la consulta de @ Dudu
La consulta inteligente de @ Dudu podría mejorarse con
generate_subscripts()
:También se usa
LEFT JOIN LATERAL ... ON true
para preservar posibles filas con valores NULL.Función PL / pgSQL
Lógica similar a la rCTE. Sustancialmente más simple y más rápido que lo que tienes:
El
OUT
parámetro se devuelve al final de la función automáticamente.No hay necesidad de inicializar
result
, porqueNULL::text[] || text 'a' = '{a}'::text[]
.Esto solo funciona si
'a'
se escribe correctamente.NULL::text[] || 'a'
(literal de cadena) generaría un error porque Postgres elige elarray || array
operador.strpos()
devuelve0
si no se encuentra ningún punto, por lo queright()
devuelve una cadena vacía y el ciclo terminaEsta es probablemente la más rápida de todas las soluciones aquí.
Todos ellos funcionan en Postgres 9.3+ (a excepción de la notación de corte de matriz corta . Agregué un límite superior en el violín para que funcione en la página 9.3:. )
arr[3:]
arr[3:999]
SQL Fiddle.
Enfoque diferente para optimizar la búsqueda
Estoy con @ jpmc26 (y usted mismo): un enfoque completamente diferente será preferible. Me gusta la combinación de jpmc26
reverse()
y atext_pattern_ops
.Un índice de trigrama sería superior para coincidencias parciales o difusas. Pero como solo le interesan las palabras completas , la búsqueda de texto completo es otra opción. Espero un tamaño de índice sustancialmente menor y, por lo tanto, un mejor rendimiento.
pg_trgm y FTS admiten consultas que no distinguen entre mayúsculas y minúsculas , por cierto.
Los nombres de host como
q.x.t.com
ot.com
(palabras con puntos en línea) se identifican como tipo "host" y se tratan como una sola palabra. Pero también hay coincidencia de prefijos en FTS (que a veces parece pasarse por alto). El manual:Usando la idea inteligente de @ jpmc26 con
reverse()
, podemos hacer que esto funcione:Que es compatible con un índice:
Tenga en cuenta la
'simple'
configuración: no queremos que se utilicen las derivaciones o tesauros con la'english'
configuración predeterminada .Alternativamente (con una mayor variedad de consultas posibles) podríamos usar la nueva capacidad de búsqueda de frases de búsqueda de texto en Postgres 9.6. Las notas de lanzamiento:
Consulta:
Reemplace dot (
'.'
) con espacio (' '
) para evitar que el analizador clasifique 't.com' como nombre de host y, en su lugar, use cada palabra como lexema separado.Y un índice coincidente para acompañarlo:
fuente
Se me ocurrió algo semi-viable, pero me encantaría recibir comentarios sobre el enfoque. He escrito muy poco PL / pgSQL, así que siento que todo lo que hago es bastante hacky y me sorprende cuando funciona.
Sin embargo, aquí es donde llegué a:
Esto funciona así:
fuente
Yo uso la función de ventana:
Resultado:
fuente
Una variante de la solución de @Dudu Markovitz, que también funciona con versiones de PostgreSQL que (todavía) no reconocen [i:]:
fuente