miércoles, 13 de junio de 2012

Búsquedas con acentos en SQL Server

Supongamos que tenemos la siguiente tabla llamada “Persona” creada en una base de datos con intercalación (collation en inglés) sensible a acentos (AS - “Accent Sensitive”), por ejemplo, Modern_Spanish_CI_AS.

Lanzamos la siguiente consulta sobre la tabla para obtener todos los resultados posibles:

-- Obtenemos datos de Personas
SELECT * FROM Persona

Y obtenemos el conjunto de resultados que muestra la imagen.


Imaginemos ahora que queremos saber cuántas personas tenemos con el primer apellido “Pérez” así que lanzamos la siguiente consulta haciendo una búsqueda con acentos:

-- Obtenemos datos de Personas con primer apellido = Pérez
SELECT * FROM Persona WHERE Apellido1 = ‘Pérez’

La consulta nos devuelve 1 resultado pero lo que realmente queríamos es saber los Pérez/Perez/Pèrez, que la consulta nos devolviera 3 registros (podría ser el caso típico, por ejemplo, de una web con un formulario para dar de alta personas donde los usuarios los introducen con/sin acentos Pérez/Perez/Pèrez y otro formulario de búsqueda por determinados campos).

La solución pasa por tratar la intercalación como no sensible a acentos (AI – “Accent Insensitive”). Buscando por distintos foros encontrarás soluciones variadas, a continuación comentamos un par y sus pegas así como una tercera que probablemente sería lo más adecuado en este caso.


Solución 1: Cambiar la intercalación de la base de datos de AS a AI.

En nuestro caso de Modern_Spanish_CI_AS a Modern_Spanish_CI_AI. Para ello lanzaríamos la consulta:

-- Cambiar intercalación base de datos
ALTER DATABASE [nombreBaseDatos] COLLATE Modern_Spanish_CI_AI
GO

Realmente esta solución nos devolvería 3 registros en nuestra consulta anterior pero se me plantean un par de problemas:
  1. ¿Nuestro usuario SQL Server tiene los permisos necesarios para cambiar la intercalación de la la base de datos? Quizás no seamos propietario de la base de datos, administrador o tengamos un rol que permita hacerlo.
  2. ¿Sabemos cómo afecta el cambio de intercalación a las aplicaciones que acceden a esta base de datos? Quizás no sólo se esté ejecutando nuestra aplicación…

Solución 2: Cambiar la intercalación de la columna de AS a AI.

En nuestro caso lo que haríamos sería cambiar la intercalación de la columna Apellido1 ejecutando la siguiente consulta:

-- Cambiar intercalación base una columna
ALTER TABLE Persona 
ALTER COLUMN Apellido1 varchar(20) COLLATE Modern_Spanish_CI_AI
GO

Esta solución también nos devolvería 3 registros en nuestra consulta anterior pero, además de los posibles problemas de la solución anterior, si:
  1. Ejecutamos una consulta del siguiente estilo para obtener los 2 apellidos de una persona en 1 única columna.

    -- Obtener apellidos concatenados
    SELECT Apellido1 + ‘ ’ + Apellido2 AS Apellidos 
    FROM Persona
    

  2. Ejecutamos una consulta de este estilo para saber todos los apellidos que existen en la tabla.

    -- Obtener los distintos apellidos existentes
    SELECT Apellido1 FROM Persona
    UNION ALL
    SELECT Apellido2 FROM Persona
    

Obtendremos el siguiente error:
Mens. 457, Nivel 16, Estado 1, Línea 1
No se puede realizar la conversión implícita del valor varchar a varchar porque la intercalación del valor no está resuelta a causa de un conflicto de intercalación.
Esto se produce porque las columnas Apellido1 y Apellido2 tienen diferentes intercalaciones. Igual que las operaciones anteriores que producen error tampoco se puede hacer JOIN entre 2 columnas con diferente intercalación,...


Solución 3: No modificar la intercalación y especificarla en el SELECT.

Sin duda a priori es una solución más “limpia” que las anteriores, se trata de no modificar la intercalación en ningún caso y especificar en nuestra consulta la intercalación necesaria. Así, nuestra consulta original:

-- Obtenemos datos de Personas con primer apellido = Pérez
SELECT * FROM Persona WHERE Apellido1 = ‘Pérez’

Quedaría de la siguiente manera:

-- Obtenemos datos teniendo en cuenta intercalación
SELECT * FROM Persona 
WHERE Apellido1 = ‘Pérez’ COLLATE Modern_Spanish_CI_AI

La consulta anterior nos devolvería los 3 registros; de hecho, la consulta anterior nos devolverá los mismos resultados que cualquiera de estas dos:

-- Consultas equivalentes
SELECT * FROM Persona 
WHERE Apellido1 = ‘Perez’ COLLATE Modern_Spanish_CI_AI

SELECT * FROM Persona 
WHERE Apellido1 = ‘Pèrez’ COLLATE Modern_Spanish_CI_AI

Lo que significa que utilizando este método sea cual sea el valor que pongamos en el WHERE (Pérez/Perez/Pèrez) siempre nos devolverá los 3 registros, independientemente de como pongamos o no el acento. Dentro de areaTIC puedes encontrar otros artículos interesantes, no dudes en consultar nuestro archivo.


LECTURAS RELACIONADAS RECOMENDADAS POR AREATIC.NET

3 comentarios:

FenriV dijo...

muchas gracias, muy buen articulo, me ayudo mucho

saludos

David Berdié Escolano dijo...

me alegro de que te haya sido útil!!

mario fierro dijo...

Justo lo que estaba buscando, muchas gracias.

Saludos

Publicar un comentario