Postgresql SELECT si la cadena contiene

105

Entonces tengo un en mi Postgresql:

TAG_TABLE
==========================
id            tag_name       
--------------------------
1             aaa
2             bbb
3             ccc

Para simplificar mi problema, lo que quiero hacer es SELECCIONAR 'id' de TAG_TABLE cuando una cadena "aaaaaaaa" contiene el 'tag_name'. Entonces, idealmente, solo debería devolver "1", que es el ID para el nombre de etiqueta "aaa"

Esto es lo que estoy haciendo hasta ahora:

SELECT id FROM TAG_TABLE WHERE 'aaaaaaaaaaa' LIKE '%tag_name%'

Pero obviamente, esto no funciona, ya que postgres piensa que '% tag_name%' significa un patrón que contiene la subcadena 'tag_name' en lugar del valor de datos real en esa columna.

¿Cómo paso el tag_name al patrón?

usuario2436815
fuente

Respuestas:

131

Debe usar 'tag_name' fuera de las comillas; luego se interpreta como un campo del registro. Concatenar usando '||' con los signos de porcentaje literal:

SELECT id FROM TAG_TABLE WHERE 'aaaaaaaa' LIKE '%' || tag_name || '%';
Frans van Buul
fuente
5
¿Qué pasa cuando tag_name es "; drop table TAG_TABLE; --"?
Denis de Bernardy
24
@Denis: No pasa nada. No obtiene ninguna fila, porque la WHEREcláusula se evalúa como FALSE. La declaración no es dinámica, solo los valores están concatenados, no hay posibilidad de inyección SQL.
Erwin Brandstetter
1
¿No debería invertirse el orden de aaaa y tag_name?
Quiero
@ user151496 No porque el patrón debe ir en el lado derecho de la LIKEpalabra clave.
jpmc26
4
Tenga en cuenta que el uso de variables en un LIKEpatrón puede tener consecuencias no deseadas cuando esas variables contienen guiones bajos (_) o caracteres de porcentaje (%). Puede ser necesario escapar de estos caracteres, por ejemplo con esta función: CREATE OR REPLACE FUNCTION quote_for_like(text) RETURNS text LANGUAGE SQL IMMUTABLE AS $$ SELECT regexp_replace($1, '([\%_])', '\\\1', 'g'); $$;(del usuario MatheusOl del canal de IRC #postgresql en Freenode).
Martin von Wittich
46

Personalmente prefiero la sintaxis más simple del operador ~.

SELECT id FROM TAG_TABLE WHERE 'aaaaaaaa' ~ tag_name;

Vale la pena leer Diferencia entre LIKE y ~ en Postgres para comprender la diferencia. '

Keithhackbarth
fuente
2
Esto funciona solo cuando tag_namees un REGEX adecuado. Bastante arriesgado.
Jakub Fedyczak
@JakubFedyczak para que coincida con el tag_name literal que puede usar y ***=que se menciona en postgresql.org/docs/current/static/functions-matching.html . Sin embargo, he descubierto que es mucho más lento en comparación con strpos/ positionsolutions.
phunehehe
27

Una forma adecuada de buscar una subcadena es usar la positionfunción en lugar de la likeexpresión, lo que requiere escapar %, _y un carácter de escape ( \por defecto):

SELECT id FROM TAG_TABLE WHERE position(tag_name in 'aaaaaaaaaaa')>0;
Tometzky
fuente
Esta es la forma correcta de hacer esto. Nadie debería usar los enfoques hacky regex.
khol
LIKEy ILIKEpuede utilizar giníndices. positionno puedo.
Eugene Pakhomov
14

Además de la solución con 'aaaaaaaa' LIKE '%' || tag_name || '%'hay position(orden inverso de argumentos) y strpos.

SELECT id FROM TAG_TABLE WHERE strpos('aaaaaaaa', tag_name) > 0

Además de lo que es más eficiente (LIKE parece menos eficiente, pero un índice puede cambiar las cosas), hay un problema menor con LIKE: tag_name por supuesto no debería contener %y especialmente _(comodín de carácter único), para no dar falsos positivos.

Joop Eggen
fuente
2
Tuve que reemplazar strpos con position, ya que strpos siempre devolvía 0 para mí
jcf
-2
SELECT id FROM TAG_TABLE WHERE 'aaaaaaaa' LIKE '%' || "tag_name" || '%';

tag_name debe estar entre comillas, de lo contrario, dará un error ya que el nombre de la etiqueta no existe

Shweta Verma
fuente
2
Esto es exactamente lo contrario de la respuesta aceptada . Estás concatenando como una cadena mientras que necesita ser una columna ...
Suraj Rao