SQL · Guías · 30 de octubre de 2026 · 12 min de lectura
LIKE en SQL: comodines % y _, ILIKE, NOT LIKE, tildes y caracteres de escape

LIKE filtra texto por un patrón en lugar de por un valor exacto. Un patrón tiene dos comodines: % equivale a cualquier secuencia de caracteres, también a ninguna, y _ equivale a exactamente un carácter. Así, city LIKE 'Mos%' encuentra todas las ciudades que empiezan por “Mos”, model LIKE '%300' los modelos que terminan en “300” y airport_name LIKE '%ovo%' los nombres que contienen “ovo” en cualquier posición. En PostgreSQL y DuckDB LIKE distingue mayúsculas y minúsculas; ILIKE es la versión que no las distingue. NOT LIKE invierte la condición, pero ninguno de los dos devuelve jamás filas en las que la columna sea NULL.
Todos los ejemplos usan la base de datos de una aerolínea del curso de SQL de Nomto, con sintaxis de PostgreSQL sobre DuckDB (el motor de la sala de SQL de Nomto). Los valores están en inglés y cada tabla de resultados es la salida real de la consulta.
Sintaxis de LIKE
SELECT columns
FROM table
WHERE text_column LIKE 'pattern';
El patrón es una cadena normal entre comillas simples. Sin comodines, LIKE se comporta como =; lo que lo hace útil son % y _. El patrón tiene que coincidir con el valor completo, y por eso importa tanto dónde pones el %:
| patrón | significa | encuentra |
|---|---|---|
'Mos%' |
empieza por “Mos” | Moscow |
'%sk' |
termina en “sk” | Omsk, Tomsk, Irkutsk |
'%ovo%' |
contiene “ovo” | Domodedovo, Pulkovo |
'_____' |
exactamente cinco caracteres | Kazan, Sochi |
'S_r%' |
S, un carácter cualquiera, r y luego lo que sea | Saratov, Surgut |
El comodín %: empieza por, termina en, contiene
Empieza por
SELECT airport_code, airport_name, city
FROM airports
WHERE city LIKE 'Mos%'
ORDER BY airport_code;
| airport_code | airport_name | city |
|---|---|---|
| DME | Domodedovo | Moscow |
| SVO | Sheremetyevo | Moscow |
| VKO | Vnukovo | Moscow |
Termina en
SELECT model
FROM aircrafts
WHERE model LIKE '%300'
ORDER BY model;
| model |
|---|
| Boeing 737-300 |
| Boeing 767-300 |
| Boeing 777-300 |
Contiene
Un % a cada lado significa “en cualquier parte de la cadena”:
SELECT airport_name, city
FROM airports
WHERE airport_name LIKE '%ovo%'
ORDER BY airport_name
LIMIT 6;
| airport_name | city |
|---|---|
| Domodedovo | Moscow |
| Ivanovo South | Ivanovo |
| Kemerovo | Kemerovo |
| Khomutovo | Yuzhno-Sakhalinsk |
| Khrabrovo | Kaliningrad |
| Koltsovo | Yekaterinburg |
El resultado completo tiene 12 aeropuertos; aquí la consulta se corta en seis filas.
“Contiene” suele atrapar más de lo que pretendes. Los nombres de pasajero se guardan como “NOMBRE APELLIDO” en mayúsculas. Si buscas el apellido Ivanov con '%IVANOV%' salen 14.040 billetes, pero con '% IVANOV' (un espacio y el apellido justo al final) solo 7.539:
SELECT CASE WHEN passenger_name LIKE '% IVANOV' THEN 'IVANOV'
WHEN passenger_name LIKE '% IVANOVA' THEN 'IVANOVA'
ELSE 'other' END AS surname,
COUNT(*) AS tickets
FROM tickets
WHERE passenger_name LIKE '%IVANOV%'
GROUP BY surname
ORDER BY tickets DESC;
| surname | tickets |
|---|---|
| IVANOV | 7539 |
| IVANOVA | 6501 |
El patrón laxo se trajo sin avisar también la forma femenina, Ivanova. Es lo mismo que pasaría al buscar '%GARCIA%' en una base de clientes y encontrarte también “GARCIARENA”. Ancla el patrón todo lo que los datos permitan.
El comodín _: exactamente un carácter
_ representa un carácter, ni más ni menos. Cinco guiones bajos encuentran las ciudades de cinco letras:
SELECT city
FROM airports
WHERE city LIKE '_____'
ORDER BY city;
| city |
|---|
| Anapa |
| Chita |
| Kazan |
| Kirov |
| Kursk |
| Kyzyl |
| Mirny |
| Nadym |
| Penza |
| Pskov |
| Sochi |
| Tomsk |
| Ukhta |
Combinando los dos comodines: 'S_r%' significa S, un carácter cualquiera, r y después lo que sea.
SELECT city FROM airports WHERE city LIKE 'S_r%' ORDER BY city;
| city |
|---|
| Saransk |
| Saratov |
| Strezhevoy |
| Surgut |
Strezhevoy no es un error: S, luego la “t” ocupa el guion bajo y después la “r”. _ acepta cualquier carácter, consonantes incluidas.
Buscar sin distinguir mayúsculas: ILIKE y LOWER
En PostgreSQL y DuckDB, LIKE compara los caracteres tal cual, así que una minúscula no coincide con una mayúscula:
SELECT 'ABC' LIKE 'abc' AS like_result, 'ABC' ILIKE 'abc' AS ilike_result;
| like_result | ilike_result |
|---|---|
| false | true |
WHERE city LIKE 'mos%' no devuelve nada; WHERE city ILIKE 'mos%' devuelve los tres aeropuertos de Moscú. ILIKE no es SQL estándar: existe en PostgreSQL, DuckDB y Snowflake, pero no en MySQL ni en SQL Server. La forma portable es pasar los dos lados a minúsculas:
SELECT city FROM airports WHERE lower(city) LIKE 'mos%';
En MySQL y SQL Server, que distinga o no mayúsculas depende de la intercalación (collation) de la columna, y las de por defecto no las distinguen, así que allí un simple LIKE 'mos%' ya encuentra Moscow. Esa diferencia sorprende a menudo cuando una consulta pasa de una base de datos a otra.
Tildes y ñ: buscar “Bogotá” escribiendo “bogota”
En español el problema no son solo las mayúsculas. ILIKE ignora la caja, pero no las tildes:
SELECT 'Bogotá' ILIKE 'bogota%' AS ilike_result,
strip_accents('Bogotá') ILIKE 'bogota%' AS sin_tildes;
| ilike_result | sin_tildes |
|---|---|
| false | true |
Cada motor lo resuelve a su manera:
- DuckDB:
strip_accents(col), como en el ejemplo. - PostgreSQL: la extensión
unaccent(CREATE EXTENSION unaccent;y luegounaccent(col) ILIKE unaccent('bogotá%')). - SQL Server: una intercalación que no distinga acentos, la que termina en
_CI_AI(por ejemplo,col COLLATE Modern_Spanish_CI_AI LIKE 'bogota%').CIes case insensitive yAI, accent insensitive. - MySQL 8: la intercalación por defecto,
utf8mb4_0900_ai_ci, ya ignora tildes y mayúsculas. Ojo: también tratanyñcomo la misma letra, así queLIKE '%pena%'encuentra “peña”. Si eso molesta, usautf8mb4_es_0900_ai_ci, que considera la ñ una letra propia.
NOT LIKE y por qué desaparecen las filas con NULL
NOT LIKE se queda con las filas que no coinciden. La trampa: si la columna es NULL, tanto LIKE como NOT LIKE devuelven NULL, y WHERE descarta la fila en los dos casos. Más o menos la mitad de los billetes tienen un email en contact_data; vamos a separarlos según la dirección contenga o no un guion bajo (cómo funciona \_ lo explica la siguiente sección):
SELECT COUNT(*) FILTER (WHERE contact_data->>'email' LIKE '%\_%' ESCAPE '\') AS with_underscore,
COUNT(*) FILTER (WHERE contact_data->>'email' NOT LIKE '%\_%' ESCAPE '\') AS without_underscore,
COUNT(*) FILTER (WHERE contact_data->>'email' IS NULL) AS no_email,
COUNT(*) AS total
FROM tickets;
| with_underscore | without_underscore | no_email | total |
|---|---|---|---|
| 87828 | 111255 | 167650 | 366733 |
LIKE más NOT LIKE suman 199.083, no 366.733: los 167.650 billetes sin email no están en ningún grupo. Si “no coincide” debe incluir “no tiene valor”, dilo de forma explícita: WHERE email NOT LIKE '%\_%' ESCAPE '\' OR email IS NULL, o WHERE COALESCE(email, '') NOT LIKE .... La guía de NULL en SQL explica la lógica de tres valores que hay detrás.
Escapar: cómo buscar un % o un _ literal
Como % y _ son comodines, para buscar esos caracteres tal cual necesitas un carácter de escape. Sin él, “emails que contienen un guion bajo” coincide con todos los emails:
SELECT COUNT(*) AS emails,
COUNT(*) FILTER (WHERE email LIKE '%_%') AS unescaped,
COUNT(*) FILTER (WHERE email LIKE '%\_%' ESCAPE '\') AS escaped
FROM (SELECT contact_data->>'email' AS email FROM tickets) t
WHERE email IS NOT NULL;
| emails | unescaped | escaped |
|---|---|---|
| 199083 | 199083 | 87828 |
'%_%' significa “al menos un carácter, el que sea”, y eso lo cumple cualquier cadena no vacía. Con ESCAPE '\', la secuencia \_ significa un guion bajo de verdad.
El carácter de escape por defecto cambia de una base de datos a otra, y aquí es donde se rompen las consultas copiadas:
- PostgreSQL usa la barra invertida como escape por defecto, así que
LIKE '%\_%'funciona sin cláusulaESCAPE. - DuckDB no tiene escape por defecto. El mismo
LIKE '%\_%'sinESCAPEdevuelve 0 filas con estos datos, porque busca una barra invertida real. Escribe siempreESCAPE '\'. - MySQL usa la barra invertida por defecto, igual que PostgreSQL.
- SQL Server tampoco tiene escape por defecto, pero permite meter el comodín entre corchetes:
LIKE '%[_]%'. En otras bases de datos los corchetes son caracteres normales (DuckDB devuelve 0 filas con ese patrón).
Una cláusula ESCAPE explícita funciona en todas, y puedes elegir el carácter que quieras, por ejemplo LIKE '%!_%' ESCAPE '!'.
Varios patrones a la vez
LIKE admite un solo patrón. Para varios, combina condiciones con OR y ponlas entre paréntesis si en el WHERE hay más condiciones:
SELECT city
FROM airports
WHERE (city LIKE 'Nov%' OR city LIKE 'Nor%')
ORDER BY city;
| city |
|---|
| Norilsk |
| Novokuznetsk |
| Novosibirsk |
| Novy Urengoy |
Sin paréntesis, WHERE a AND b OR c se lee como (a AND b) OR c, una fuente clásica de filas de más.
PostgreSQL acepta también city LIKE ANY (ARRAY['Nov%', 'Nor%']). DuckDB no: falla con Unsupported comparison "~~" for ANY/ALL subquery, así que en la sala de SQL de Nomto usa OR.
Cuando los patrones se complican, pasa a expresiones regulares. En DuckDB, regexp_matches(city, '^(Nov|Nor|Noy)') devuelve Norilsk, Novokuznetsk, Novosibirsk, Novy Urengoy y Noyabrsk. PostgreSQL usa el operador city ~ '^(Nov|Nor|Noy)'. Cuidado con ~ en DuckDB: allí la expresión tiene que coincidir con la cadena entera, así que city ~ '^Nov' no devuelve nada y necesitarías city ~ 'Nov.*'.
LIKE frente a =
= |
LIKE |
|
|---|---|---|
| Compara con | el valor exacto | un patrón con % y _ |
| Comodines | ninguno (% es un carácter normal) |
%, _ |
| Velocidad | siempre puede usar un índice | solo usa índice con un prefijo fijo |
| Uso típico | status = 'Arrived' |
city LIKE 'Nov%' |
Usa = siempre que conozcas el valor completo. LIKE sin comodines da el mismo resultado que = en PostgreSQL y DuckDB ('abc ' LIKE 'abc' y 'abc ' = 'abc' son falsos los dos). En SQL Server, = ignora los espacios finales pero LIKE los tiene en cuenta, así que allí 'abc ' = 'abc' es verdadero y 'abc' LIKE 'abc ' es falso.
LIKE también funciona con fechas convertidas a texto, CAST(scheduled_departure AS VARCHAR) LIKE '2026-08-01%', y aquí da los mismos 568 vuelos que un rango. Aun así, quédate con el rango, scheduled_departure >= '2026-08-01' AND scheduled_departure < '2026-08-02': conserva el tipo de la columna, funciona en cualquier base de datos sea cual sea el formato de fecha como texto (en España y Latinoamérica es habitual ver 01/08/2026 al exportar) y puede usar un índice. Más detalles en la guía de funciones de fecha en SQL.
Rendimiento de LIKE
En una tabla grande, cómo empieza el patrón decide lo rápido que va:
- Un prefijo fijo (
'Mos%') puede usar un índice B-tree, porque todos los valores que coinciden están juntos en orden. En PostgreSQL esto funciona sin más solo con la intercalaciónC; con otras (comoes_ES.UTF-8) necesitas un índice creado context_pattern_ops(ovarchar_pattern_ops). - Un comodín al principio (
'%ovo'o'%ovo%') no puede usar un índice normal: la base de datos tiene que leer todos los valores. En PostgreSQL, un índice de trigramas (la extensiónpg_trgmcon un índice GIN o GiST) acelera las búsquedas de “contiene”. lower(col) LIKE ...eILIKEno usan un índice simple sobrecol. En PostgreSQL, crea un índice sobrelower(col)para lo primero;ILIKEnecesita un índice de trigramas.
Los motores analíticos columnares como DuckDB, BigQuery o Snowflake recorren columnas muy deprisa de todos modos, así que con un conjunto de datos del tamaño del curso no notarás la diferencia. En una tabla de producción de PostgreSQL, MySQL o SQL Server con millones de filas, sí.
Errores frecuentes
- Olvidar el comodín.
WHERE city LIKE 'Mos'solo encuentra una ciudad que se llame exactamente “Mos”. - Comillas dobles alrededor del patrón. En PostgreSQL y DuckDB,
"Mos%"es un nombre de columna, no una cadena. Usa comillas simples. %a los dos lados cuando querías un prefijo. Es más lento y encuentra de más, como muestra el ejemplo de Ivanov/Ivanova.- Un
_sin escapar en el patrón.LIKE 'view_%'encuentra “view_offer”, pero también “viewX” o “views”; escápalo si te refieres a un guion bajo real. - Esperar que
NOT LIKEdevuelva las filas con NULL. No lo hace nunca; añadeOR col IS NULL. - Olvidar las tildes.
ILIKE 'bogota%'no encuentra “Bogotá”; quita los acentos o usa una intercalación_AI. - Mezclar
ANDyORsin paréntesis al combinar varios patrones.
Para extraer partes de una cadena una vez encontrada (SPLIT_PART, SUBSTRING, REPLACE), mira la guía de funciones de texto en SQL. Los filtros con patrones también están en la chuleta de SQL.
Preguntas frecuentes
¿Qué significa % en LIKE?
% equivale a cualquier secuencia de caracteres, también a una vacía. 'Mos%' coincide con “Mos”, “Moscow” y cualquier otra cosa que empiece por “Mos”. _ equivale a exactamente un carácter.
¿LIKE distingue mayúsculas y minúsculas?
En PostgreSQL, DuckDB y Oracle, sí. En MySQL y SQL Server depende de la intercalación, y las de por defecto no las distinguen. Para no distinguirlas en PostgreSQL o DuckDB usa ILIKE, o lower(col) LIKE 'patrón' en cualquier base de datos.
¿Cómo busco sin tener en cuenta las tildes?
En DuckDB, strip_accents(col); en PostgreSQL, la extensión unaccent; en SQL Server, una intercalación _CI_AI como Modern_Spanish_CI_AI. En MySQL 8 la intercalación por defecto ya ignora tildes, aunque también confunde n con ñ.
¿Cómo escapo un guion bajo o un signo de porcentaje en LIKE?
Pon un carácter de escape delante y decláralo con ESCAPE: col LIKE '%\_%' ESCAPE '\' encuentra valores con un guion bajo real. PostgreSQL y MySQL usan la barra invertida por defecto; DuckDB y SQL Server no, así que la cláusula ESCAPE explícita es la forma portable. SQL Server acepta además '%[_]%'.
¿Puedo usar LIKE con varios valores?
No directamente. Combina condiciones con OR (city LIKE 'Nov%' OR city LIKE 'Nor%'), usa LIKE ANY (ARRAY[...]) en PostgreSQL o pasa a una expresión regular.
¿LIKE es más lento que =?
Un LIKE con prefijo fijo puede usar un índice y es casi tan rápido como =. Un patrón que empieza por % tiene que revisar todas las filas, lo que en tablas grandes es mucho más lento salvo que añadas un índice de trigramas (PostgreSQL) o de texto completo.
¿LIKE funciona con números?
En PostgreSQL primero hay que convertir: CAST(col AS TEXT) LIKE '12%'. MySQL convierte solo. Para números, una condición de rango (col BETWEEN 1200 AND 1299) casi siempre es mejor opción.
También en English, Português (Brasil), Tiếng Việt, 简体中文