En una entrada anterior, vimos cómo implementar y utilizar la distancia de Levenshtein en SQL Server para realizar búsquedas aproximadas. Esta técnica permite contar el número de ediciones necesarias (inserciones, eliminaciones o sustituciones) para transformar una cadena de texto en otra. Gracias a esto, pudimos comparar una cadena específica con los valores de una columna y devolver los registros ordenados según la ”similaritud”. Este enfoque funciona bien cuando la comparación es directa, como en el caso de "Jhon" y "John", donde la distancia de edición es 2. Sin embargo, este enfoque no resulta adecuado cuando se trata de buscar palabras similares en SQL Server dentro de frases o cadenas más largas.
Sin embargo, en escenarios del mundo real, los usuarios no siempre introducen una palabra exacta. A menudo, escriben solo una parte de un nombre, una frase o una descripción. Por ejemplo:
"Jhon""John Smith"Aquí, el nombre "John Smith" contiene una palabra similar a la introducida por el usuario ("Jhon"), pero no coincide con toda la cadena. Si usamos nuestra función edit_distance tal como fue implementada, esta comparará "Jhon" con "John Smith" como una única cadena, lo cual produce una distancia de edición artificialmente alta.
Para solucionar este problema, necesitamos un enfoque más refinado: analizar las palabras individuales dentro del campo de texto y buscar coincidencias aproximadas con la palabra proporcionada. Esta es precisamente la estrategia que exploraremos en esta entrada.
Tabla de contenidos
La solución propuesta consiste en descomponer el contenido de los campos de texto en palabras individuales y comparar cada una con el término introducido por el usuario. Empleando para ello la función que fue implementada anteriormente para obtener la distancia de Levenshtein. Luego, con estos resultados, identificaremos las palabras más parecidas y devolveremos los registros más relevantes, priorizando aquellos con mayor similitud.
En resumen, los pasos a seguir serían:
Aunque SQL Server no cuenta con una función nativa para dividir cadenas en palabras, podemos utilizar funciones como STRING_SPLIT para lograr este objetivo. A continuación, te muestro cómo construir esta solución paso a paso.
Primero, necesitamos una función que divida un campo de texto en palabras individuales. SQL Server 2016 y versiones posteriores incluyen la función STRING_SPLIT, que nos permite dividir una cadena usando un delimitador. Como las palabras suelen estar separadas por espacios, podemos usar esta función directamente.
Aquí tienes un ejemplo básico de cómo dividir una cadena en palabras:
SELECT value AS palabra
FROM STRING_SPLIT('John Smith', ' '); El resultado de esta consulta será:
| palabra |
|---|
| John |
| Smith |
Ahora que podemos dividir los campos en palabras, el siguiente paso es comparar cada palabra individual con el término introducido por el usuario. Para esto, utilizaremos la función edit_distance que creamos en la entrada anterior.
Asumiendo que se tiene una tabla clientes con los campos id y nombre, la siguiente consulta que realiza esta comparación y devuelve los registros ordenados por distancia:
SELECT DISTINCT
c.id,
c.nombre,
MIN(ed.distancia) AS distancia_minima
FROM clientes AS c
CROSS APPLY STRING_SPLIT(c.nombre, ' ') AS s
CROSS APPLY (
SELECT dbo.edit_distance(s.value, 'Jhon') AS distancia
) AS ed
WHERE ed.distancia <= 3
GROUP BY c.id, c.nombre
ORDER BY distancia_minima ASC; Donde cada una de las lineas realiza una operación en el proceso:
STRING_SPLIT(c.nombre, ' '): Divide el campo nombre (por ejemplo, “John Smith”) en palabras individuales.CROSS APPLY: Permite aplicar la función STRING_SPLIT a cada fila de la tabla clientes.dbo.edit_distance(s.value, 'Jhon'): Calcula la distancia de Levenshtein entre cada palabra (s.value) y el término a buscar (“Jhon”).WHERE ed.distancia <= 3: Filtra solo las palabras cuya distancia de edición sea menor o igual a 3 (lo que se puede ajustar según la tolerancia deseada).GROUP BY y MIN: Agrupa los resultados por id y nombre y devuelve la distancia más pequeña para cada registro.ORDER BY distancia_minima ASC: Ordena los resultados para mostrar primero las coincidencias más cercanas.Supongamos que tenemos la siguiente tabla clientes:
| id | nombre |
|---|---|
| 1 | John Smith |
| 2 | Jonathan Doe |
| 3 | Jhonson Martinez |
| 4 | Alice Johnson |
| 5 | Johan Peterson |
Si el usuario busca “Jhon”, la consulta anterior devolverá:
| id | nombre | distancia minima |
|---|---|---|
| 1 | John Smith | 2 |
| 5 | Johan Peterson | 2 |
| 3 | Jhonson Martinez | 3 |
| 4 | Alice Johnson | 3 |
Mostrandose primero “John Smith” y “Johan Peterson” porque son los registros que tiene palabras con una distancia de 2 respecto a campos buscado “Jhon”.
Esta solución es muy flexible, pero puede ser costosa en términos de recursos, especialmente si trabajas con tablas muy grandes. Para optimizar las búsquedas se pueden realizar aplicar algunos trucos como:
<= 3) para limitar el número de comparaciones.STRING_SPLIT y Levenshtein no se benefician directamente de índices, asegurarse de tener índices en las columnas relevantes puede mejorar el rendimiento general.Combinar la distancia de Levenshtein con la función STRING_SPLIT nos permite buscar palabras similares en SQL Server, identificando palabras individuales que son similares al término buscado. Esto soluciona el problema de las búsquedas inexactas en nombres, frases o descripciones más largas, y mejora significativamente la experiencia del usuario.
Con esta técnica, puedes ofrecer un sistema de búsqueda más inteligente y tolerante a errores tipográficos, lo que resulta especialmente útil en aplicaciones como bases de datos de clientes, sistemas de inventario o motores de búsqueda.
Si implementas esta solución, tus usuarios podrán encontrar “John Smith” incluso si escriben “Jhon” o “Johan”, logrando resultados mucho más precisos y relevantes.
Para facilitar el proceso descrito anteriormente, es útil encapsular la lógica de comparación en una función reutilizable. Esto mejora la claridad de las consultas y permite aplicar el enfoque en múltiples contextos sin tener que repetir la misma lógica una y otra vez.
A continuación, definimos una función llamada dbo.distancia_minima_entre_palabras que recibe un campo de texto (como un nombre completo) y un término de búsqueda (como "Jhon"), y devuelve la menor distancia de Levenshtein entre el término de búsqueda y cada palabra individual del campo.
Una posible implementación de esta función puede ser la que se muestra a continuación:
CREATE FUNCTION dbo.distancia_minima_entre_palabras
(
@texto NVARCHAR(MAX), -- El texto completo (por ejemplo, el nombre completo de un cliente)
@termino NVARCHAR(100) -- La palabra a buscar (por ejemplo, 'Jhon')
)
RETURNS INT
AS
BEGIN
DECLARE @distancia_minima INT;
-- Tabla temporal para almacenar las distancias de cada palabra
DECLARE @distancias TABLE (valor NVARCHAR(100), distancia INT);
-- Insertar las distancias de cada palabra comparada con el término de búsqueda
INSERT INTO @distancias (valor, distancia)
SELECT
valor,
dbo.edit_distance(valor, @termino)
FROM
STRING_SPLIT(@texto, ' ');
-- Obtener la menor distancia encontrada
SELECT
@distancia_minima = MIN(distancia)
FROM
@distancias;
RETURN @distancia_minima;
END;
GO Una vez creada la función, podemos aplicarla directamente sobre una tabla de clientes para buscar aquellos cuyos nombres contienen palabras similares a un término dado, por ejemplo, "Jhon":
SELECT
c.id,
c.nombre,
dbo.distancia_minima_entre_palabras(c.nombre, 'Jhon') AS distancia_minima
FROM
clientes AS c
WHERE
dbo.distancia_minima_entre_palabras(c.nombre, 'Jhon') <= 2
ORDER BY
distancia_minima; Este enfoque permite encontrar coincidencias como "John Smith", "Jhonny Depp" o "Jon Snow" sin requerir coincidencias exactas, pero priorizando aquellas que son más parecidas según la distancia de Levenshtein.
Nota: La imagen de este artículo fue generada utilizando un modelo de inteligencia artificial.
Hace poco publiqué una entrada en la que trataba de un sesgo bien documentado: aferrarse…
En un entrada previa explicamos qué son el WOE y el IV y por qué…
Seguimos evolucionando el laboratorio de Analytics Lane y hoy lanzamos la versión 1.1, disponible en:…
“El interés compuesto es la octava maravilla del mundo. El que lo entiende lo gana…
Tienes los datos de ventas de tres productos en dos años distintos y quieres saber…
Imagina la situación. Tu equipo lleva tres años con un modelo en producción. No es…
This website uses cookies.