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 or
y / o like
?
postgresql
performance
index
regular-expression
pattern-matching
Lucas Kauffman
fuente
fuente
s.name
indexado?name
podrí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 TO
en cualquier caso para mí.Con un índice B-Tree apropiado,
LIKE
gana 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 TO
o 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 TO
simplemente no tiene sentido . Un mestizo peculiar deLIKE
y 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_trgm
para proporcionar soporte de índice para cualquierLIKE
/ILIKE
patrón (y patrones de expresión regular simples con~
) usando un índice GIN o GiST.Detalles, ejemplo y enlaces:
pg_trgm
Tambié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 TO
Las expresiones se reescriben en expresiones regulares internamente. Entonces, para cadaSIMILAR TO
expresió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 TO
nunca .Y las expresiones simples que se pueden hacer con
LIKE
(~~
) son más rápidas con deLIKE
todos modos.SIMILAR TO
solo 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 ANALYZE
lo revela ¡Intenta con cualquier mesa tú mismo!Revela:
SIMILAR TO
ha sido reescrito con una expresión regular (~
).Máximo rendimiento para este caso particular
Pero
EXPLAIN ANALYZE
revela 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_ops
o 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 TO
por igual.Lo mismo es cierto para los índices en
varchar
tipos convarchar_pattern_ops
ochar
conbpchar_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
similar
un escaneo?EXPLAIN ANALYZE
muestra 2 escaneos de índice de mapa de bits. Se pueden combinar múltiples escaneos de índice de mapa de bits con bastante rapidez.OR
conUNION ALL
o reemplazarname LIKE 'B%'
conname >= 'B' AND name <'C'
en Postgres?UNION
no, pero sí, combinar los rangos en unaWHERE
clá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.