Tuve que escribir una consulta simple donde busco el nombre de las personas que comienzan con una B o una D:
SELECT s.name 
FROM spelers s 
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
Me preguntaba si hay una manera de reescribir esto para ser más eficiente. ¿Entonces puedo evitar ory / o like?
                    
                        postgresql
                                performance
                                index
                                regular-expression
                                pattern-matching
                                
                    
                    
                        Lucas Kauffman
fuente
                
                fuente

s.nameindexado?namepodría ser útil aquí si le importa el rendimiento.Respuestas:
Su consulta es más o menos la óptima. La sintaxis no será mucho más corta, la consulta no será mucho más rápida:
Si realmente quiere acortar la sintaxis , use una expresión regular con ramas :
O un poco más rápido, con una clase de personaje :
Una prueba rápida sin índice produce resultados más rápidos que
SIMILAR TOen cualquier caso para mí.Con un índice B-Tree apropiado,
LIKEgana esta carrera por orden de magnitud.Lea los conceptos básicos sobre la coincidencia de patrones en el manual .
Índice para un rendimiento superior
Si le preocupa el rendimiento, cree un índice como este para tablas más grandes:
Hace este tipo de consulta más rápido por orden de magnitud. Se aplican consideraciones especiales para el orden de clasificación específico de la localidad. Lea más sobre las clases de operador en el manual . Si está utilizando la configuración regional "C" estándar (la mayoría de la gente no lo hace), un índice simple (con la clase de operador predeterminada) servirá.
Tal índice solo es bueno para patrones anclados a la izquierda (coincidencia desde el inicio de la cadena).
SIMILAR TOo expresiones regulares con expresiones básicas ancladas a la izquierda también pueden usar este índice. Pero no con ramas(B|D)o clases de caracteres[BD](al menos en mis pruebas en PostgreSQL 9.0).Las coincidencias de trigrama o la búsqueda de texto utilizan índices especiales GIN o GiST.
Descripción general de los operadores de coincidencia de patrones
LIKE(~~) es simple y rápido pero limitado en sus capacidades.ILIKE(~~*) la variante insensible a mayúsculas y minúsculas.pg_trgm extiende el soporte de índice para ambos.
~(coincidencia de expresión regular) es potente pero más complejo y puede ser lento para cualquier cosa más que expresiones básicas.SIMILAR TOsimplemente no tiene sentido . Un mestizo peculiar deLIKEy expresiones regulares. Nunca lo uso Vea abajo.% es el operador de "similitud", proporcionado por el módulo adicional
pg_trgm. Vea abajo.@@es el operador de búsqueda de texto. Vea abajo.pg_trgm - coincidencia de trigram
Comenzando con PostgreSQL 9.1 , puede facilitar la extensión
pg_trgmpara proporcionar soporte de índice para cualquierLIKE/ILIKEpatrón (y patrones de expresión regular simples con~) usando un índice GIN o GiST.Detalles, ejemplo y enlaces:
pg_trgmTambién proporciona estos operadores :%- el operador de "similitud"<%(conmutador%>:) - el operador "word_similarity" en Postgres 9.6 o posterior<<%(conmutador%>>:) - el operador "estricta_palabra_similaridad" en Postgres 11 o posteriorBúsqueda de texto
Es un tipo especial de coincidencia de patrones con infraestructura separada y tipos de índice. Utiliza diccionarios y derivaciones y es una gran herramienta para encontrar palabras en documentos, especialmente para idiomas naturales.
La coincidencia de prefijos también es compatible:
Además de la búsqueda de frases desde Postgres 9.6:
Considere la introducción en el manual y el resumen de operadores y funciones .
Herramientas adicionales para la coincidencia de cadenas difusas
El módulo adicional fuzzystrmatch ofrece algunas opciones más, pero el rendimiento generalmente es inferior a todo lo anterior.
En particular, diversas implementaciones de la
levenshtein()función pueden ser instrumentales.¿Por qué las expresiones regulares (
~) siempre son más rápidas queSIMILAR TO?La respuesta es simple.
SIMILAR TOLas expresiones se reescriben en expresiones regulares internamente. Entonces, para cadaSIMILAR TOexpresión, hay al menos una expresión regular más rápida (que ahorra la sobrecarga de reescribir la expresión). No hay ganancia de rendimiento al usarSIMILAR TOnunca .Y las expresiones simples que se pueden hacer con
LIKE(~~) son más rápidas con deLIKEtodos modos.SIMILAR TOsolo es compatible con PostgreSQL porque terminó en los primeros borradores del estándar SQL. Todavía no se han librado de eso. Pero hay planes para eliminarlo e incluir coincidencias regexp, o eso escuché.EXPLAIN ANALYZElo revela ¡Intenta con cualquier mesa tú mismo!Revela:
SIMILAR TOha sido reescrito con una expresión regular (~).Máximo rendimiento para este caso particular
Pero
EXPLAIN ANALYZErevela más. Pruebe, con el índice mencionado anteriormente en su lugar:Revela:
Internamente, con un índice que no está al tanto de la configuración regional (
text_pattern_opso el uso de la configuración regionalC) simples expresiones de izquierda-anclado se reescriben con estos operadores patrón de texto:~>=~,~<=~,~>~,~<~. Este es el caso para~,~~oSIMILAR TOpor igual.Lo mismo es cierto para los índices en
varchartipos convarchar_pattern_opsocharconbpchar_pattern_ops.Entonces, aplicado a la pregunta original, esta es la forma más rápida posible :
Por supuesto, si por casualidad busca iniciales adyacentes , puede simplificar aún más:
La ganancia sobre el uso simple de
~o~~es pequeña. Si el rendimiento no es su requisito primordial, debe seguir con los operadores estándar, llegando a lo que ya tiene en la pregunta.fuente
similarun escaneo?EXPLAIN ANALYZEmuestra 2 escaneos de índice de mapa de bits. Se pueden combinar múltiples escaneos de índice de mapa de bits con bastante rapidez.ORconUNION ALLo reemplazarname LIKE 'B%'conname >= 'B' AND name <'C'en Postgres?UNIONno, pero sí, combinar los rangos en unaWHEREcláusula acelerará la consulta. He agregado más a mi respuesta. Por supuesto, debe tener en cuenta su configuración regional. La búsqueda local es siempre más lenta.¿Qué tal agregar una columna a la tabla? Dependiendo de sus requisitos reales:
PostgreSQL no admite columnas calculadas en tablas base a SQL Server, pero la nueva columna se puede mantener a través del disparador. Obviamente, esta nueva columna sería indexada.
Alternativamente, un índice en una expresión le daría lo mismo, más barato. P.ej:
Las consultas que coinciden con la expresión en sus condiciones pueden utilizar este índice.
De esta forma, el impacto en el rendimiento se toma cuando se crean o modifican los datos, por lo que solo puede ser apropiado para un entorno de baja actividad (es decir, muchas menos escrituras que lecturas).
fuente
Podrías intentar
Sin embargo, no tengo idea de si la expresión anterior o la original son compatibles en Postgres.
Si crea el índice sugerido, también le interesaría saber cómo se compara con las otras opciones.
fuente
Lo que he hecho en el pasado, frente a un problema de rendimiento similar, es incrementar el carácter ASCII de la última carta y hacer un ENTRE. Entonces obtienes el mejor rendimiento, para un subconjunto de la funcionalidad LIKE. Por supuesto, solo funciona en ciertas situaciones, pero para los conjuntos de datos ultra grandes en los que está buscando un nombre, por ejemplo, hace que el rendimiento pase de abismal a aceptable.
fuente
Pregunta muy antigua, pero encontré otra solución rápida a este problema:
Dado que la función ascii () solo busca el primer carácter de la cadena.
fuente
(name)?Para verificar las iniciales, a menudo uso la conversión a
"char"(con comillas dobles). No es portátil, pero es muy rápido. Internamente, simplemente desintoxica el texto y devuelve el primer carácter, y las operaciones de comparación "char" son muy rápidas porque el tipo tiene una longitud fija de 1 byte:Tenga en cuenta que la conversión a
"char"es más rápida que laascii()solución de @ Sole021, pero no es compatible con UTF8 (o cualquier otra codificación), devuelve simplemente el primer byte, por lo que solo debe usarse en casos donde la comparación es contra el viejo 7 -bit caracteres ASCII.fuente
Existen dos métodos que no se han mencionado aún para tratar estos casos:
índice parcial (o particionado, si se creó para el rango completo manualmente), más útil cuando solo se requiere un subconjunto de datos (por ejemplo, durante algún mantenimiento o temporal para algunos informes):
particionando la tabla en sí (usando el primer carácter como clave de partición): vale la pena considerar esta técnica especialmente en PostgreSQL 10+ (partición menos dolorosa) y 11+ (poda de partición durante la ejecución de la consulta).
Además, si los datos de una tabla están ordenados, uno puede beneficiarse del uso del índice BRIN (sobre el primer carácter).
fuente
Probablemente más rápido para hacer una comparación de un solo personaje:
fuente
column LIKE 'B%'será más eficiente que usar la función de subcadena en la columna.