Foros del Web » Programación para mayores de 30 ;) » Bases de Datos General » Mysql »

Función translate en mysql

Estas en el tema de Función translate en mysql en el foro de Mysql en Foros del Web. Hoy tuve un problema con una consulta que muchas veces se ha presentado en este foro y en el mundo en general. Como realizar una ...
  #1 (permalink)  
Antiguo 28/05/2009, 13:04
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Función translate en mysql

Hoy tuve un problema con una consulta que muchas veces se ha presentado en este foro y en el mundo en general.

Como realizar una consulta en la cual, no me distinga las palabras con acento en una consulta. La solución para mysql es pelear con la molesta codificación de la tabla o de la base de datos en general.

Encontré una solución muy particular y efectiva para oracle y postgres haciendo uso de la función translate.
http://www.postgresql.org/docs/8.2/s...ns-string.html
La cual basta con realizar translate(campo,'áéíóúÁÉÍÓÚ','aeiouAEIOU') para ignorar las palabras que llevan acento y permitir una consulta tanto de palabras que tienen o no acento.

Lastimosamente mysql no cuenta con una función así (o por lo menos no la encontré) así que decidí crear una que simule en parte este molesto problema.

Comparto la función, por si alguien desea usarla y la forma como deben implementarla. (Acepto sugerencias y comentarios al respecto)


EJEMPLO

Código mysql:
Ver original
  1. mysql> select nombre,sexo from persona;
  2. +----------------+------+
  3. | nombre         | sexo |
  4. +----------------+------+
  5. | maría fernanda | F    |
  6. | maria lucia    | F    |
  7. | lucía          | F    |
  8. +----------------+------+
  9. 3 rows in set (0.00 sec)

Como ven, en los registros, maria y lucia se encuentran con acento y sin acento. Para una consulta de personas llamadas lucia o maria quedaría:

Código mysql:
Ver original
  1. mysql> select nombre,sexo from persona where translate(lower(nombre)) like translate('%maria%');
  2. +----------------+------+
  3. | nombre         | sexo |
  4. +----------------+------+
  5. | maría fernanda | F    |
  6. | maria lucia    | F    |
  7. +----------------+------+
  8. 2 rows in set (0.00 sec)
  9.  
  10. mysql> select nombre,sexo from persona where translate(lower(nombre)) like translate('%lucía%');
  11. +-------------+------+
  12. | nombre      | sexo |
  13. +-------------+------+
  14. | maria lucia | F    |
  15. | lucía       | F    |
  16. +-------------+------+
  17. 2 rows in set (0.00 sec)

La función es esta:

Código mysql:
Ver original
  1. DROP FUNCTION translate;
  2. DELIMITER $$
  3.  
  4. CREATE FUNCTION translate(campo varchar(50)) RETURNS VARCHAR(50)
  5. ----------------------------------------
  6. --Declaración de variables requeridas---
  7. ----------------------------------------
  8.  
  9. --------------------------------------------------------------------
  10. --* Longitud guarda el numero de digitos de la palabra ingresada. --
  11. --* contador recorre cada posición para comparar.                 --  
  12. --* campo_aux recibe el valor de cada letra,se haya cambiado o no.--
  13. --* campo_mod es la cadena a retornar. Con ls correcciones.       --
  14. --------------------------------------------------------------------
  15.  DECLARE longitud integer;    
  16.  DECLARE contador integer;
  17.  DECLARE campo_aux varchar(50);      
  18.  DECLARE campo_mod varchar(50);    
  19.  
  20. --------------------------------
  21. --Inicialización de variables---
  22. --------------------------------
  23.  set campo_mod = '';
  24.  set campo_aux = '';
  25.  set contador=1;
  26.  set longitud=length(campo);
  27.  
  28. ---------------------------------------------------------------------------------------
  29. --En el while se compara cada posición de la palabra y verifica si tiene acento o no.--
  30. --Si tiene acento, lo cambia. sino tiene acento, lo deja tal como estaba.            --
  31. ---------------------------------------------------------------------------------------
  32.   WHILE longitud >= contador DO    
  33.    if mid(lower(campo),contador,1) = 'á' then
  34.      set campo_aux = 'a';
  35.  
  36.    elseif mid(lower(campo),contador,1) = 'é' then
  37.      set campo_aux = 'e';
  38.  
  39.    elseif mid(lower(campo),contador,1) = 'í' then
  40.      set campo_aux = 'i';
  41.  
  42.    elseif mid(lower(campo),contador,1) = 'ó' then
  43.      set campo_aux = 'o';
  44.  
  45.    elseif mid(lower(campo),contador,1) = 'ú' then
  46.      set campo_aux = 'u';
  47.    else
  48.      set campo_aux = mid(lower(campo),contador,1);
  49.   end if;
  50. ----------------------------------------------------------------
  51. --En campo_mod se va creando la cadena resultante sin acentos.--
  52. --Contador se incrementa para pasar a la siguiente posición.  --
  53. ----------------------------------------------------------------
  54.   set campo_mod = concat(campo_mod,campo_aux);
  55.   set contador = contador + 1;
  56.  end while;
  57.  
  58. return campo_mod;
  59. end$$
  60. delimiter ;

Espero les sirva.
__________________
Without data, You are another person with an opinion.
W. Edwads Deming

Última edición por huesos52; 29/05/2009 a las 08:35
  #2 (permalink)  
Antiguo 28/05/2009, 16:46
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 16 años, 4 meses
Puntos: 2658
Respuesta: Función translate en mysql

En MySQL el problema se resuelve simplemente:
1. DEfiniendo la tabla con CHARSET latin1 y COLLATION latin1_swedish_ci
2. Asignando al campo la conversión con COLLATION latin1_swedish_ci.
De ese modo el problema se resuelve por tabla de caracteres.
Prueba esto:
Código sql:
Ver original
  1. CREATE TABLE  `nombres` (
  2.   `NOMBRE` VARCHAR(100) DEFAULT NULL
  3. ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
  4.  
  5. INSERT INTO NOMBRES VALUES('MARIA LUCÍA'), ('LUCIA'), ('MARÍA'),('MARÍA FERNANDA');
  6.  
  7. SELECT *
  8. FROM nombres n
  9. WHERE NOMBRE LIKE'%MARIA%';

Eso te debería devolver los tres registros que cumplen la condición, sin importar si MARIA está acentuada o no....
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #3 (permalink)  
Antiguo 28/05/2009, 17:51
Avatar de Anubis_Slash  
Fecha de Ingreso: mayo-2009
Ubicación: aqui y haya
Mensajes: 173
Antigüedad: 14 años, 11 meses
Puntos: 5
Respuesta: Función translate en mysql

Hola

No cabe duda que todos los dias se aprende algo nuevo, muy bueno lo de huesos52 cuando ya esta creada la tabla, pero si se puede evitar eso desde el diseño como nos dice gnzsoloyo mucho mejor...

son unos crack...

saludos
  #4 (permalink)  
Antiguo 28/05/2009, 21:04
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Función translate en mysql

gnzsoloyo... te cuento que no me ha funcionado.

Código mysql:
Ver original
  1. mysql> CREATE TABLE  `nombres` (
  2.     ->   `NOMBRE` varchar(100) DEFAULT NULL
  3.     -> ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
  4. Query OK, 0 rows affected, 1 warning (0.04 sec)
  5.  
  6. mysql> INSERT INTO NOMBRES VALUES('MARIA LUCÍA'), ('LUCIA'), ('MARÍA'),('MARÍA FERNANDA');
  7. Query OK, 4 rows affected (0.00 sec)
  8. Records: 4  Duplicates: 0  Warnings: 0
  9.  
  10. mysql> SELECT *
  11.     -> FROM nombres n
  12.     -> WHERE NOMBRE LIKE'%MARIA%';
  13. +-------------+
  14. | NOMBRE      |
  15. +-------------+
  16. | MARIA LUCÍA |
  17. +-------------+
  18. 1 row in set (0.00 sec)

Es posible que hace falta algo pequeño. No estoy diciendo que no exista solución para este problema, pero por eso digo... muchas veces para algo tan simple como una simple diferenciación de acentos, tenemos que hacer variaciones en la collation y charset de las tablas y campos. sumando el tiempo que puede conllevar entender las diferencias entre cada uno de los tipos de codificación que existen. Por mi parte, he tenido bastantes problemas en este sentido, que he solucionado después de tediosas pruebas y cambios a mi base de datos.

No está de mas, contar ahora con una función que resuelva este problema en una simple ejecución.
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #5 (permalink)  
Antiguo 28/05/2009, 21:36
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 16 años, 4 meses
Puntos: 2658
Respuesta: Función translate en mysql

Cita:
No está de mas, contar ahora con una función que resuelva este problema en una simple ejecución.
Es posible, pero usar una función de ese tipo en consultas contra centenares de miles de registros en una base, puede reducir la performance bastante...

Vamos a hacer un último intento:
Código sql:
Ver original
  1. CREATE TABLE  `nombres` (
  2.   `NOMBRE` VARCHAR(100) DEFAULT NULL COLLATE latin1_spanish_ci
  3. ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE latin1_spanish_ci;
  4.  
  5. INSERT INTO NOMBRES VALUES('MARIA LUCÍA'), ('LUCIA'), ('MARÍA'),('MARÍA FERNANDA');
  6.  
  7. SELECT *
  8. FROM nombres n
  9. WHERE NOMBRE LIKE _latin1'%lucia%' COLLATE latin1_spanish_ci;
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #6 (permalink)  
Antiguo 28/05/2009, 21:45
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Función translate en mysql

Nada gnzsoloyo

Código mysql:
Ver original
  1. mysql> drop table nombres;
  2. Query OK, 0 rows affected (0.00 sec)
  3.  
  4. mysql> CREATE TABLE  `nombres` (
  5.     ->   `NOMBRE` varchar(100) DEFAULT NULL COLLATE latin1_spanish_ci
  6.     -> ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE latin1_spanish_ci;
  7. Query OK, 0 rows affected, 1 warning (0.01 sec)
  8.  
  9. mysql> INSERT INTO NOMBRES VALUES('MARIA LUCÍA'), ('LUCIA'), ('MARÍA'),('MARÍA FERNANDA');
  10. Query OK, 4 rows affected (0.00 sec)
  11. Records: 4  Duplicates: 0  Warnings: 0
  12.  
  13. mysql> SELECT *
  14.     -> FROM nombres n
  15.     -> WHERE NOMBRE LIKE _latin1'%lucia%' COLLATE latin1_spanish_ci;
  16. +--------+
  17. | NOMBRE |
  18. +--------+
  19. | LUCIA  |
  20. +--------+
  21. 1 row in set (0.00 sec)
  22.  
  23. mysql>

Estoy totalmente de acuerdo, para bastantes registros, puede presentar prooblemas de performance.
Podría existir alguna forma de optimizarla un poco?
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #7 (permalink)  
Antiguo 28/05/2009, 22:54
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Función translate en mysql

repetido..
__________________
Without data, You are another person with an opinion.
W. Edwads Deming

Última edición por huesos52; 28/05/2009 a las 22:56 Razón: repetido
  #8 (permalink)  
Antiguo 28/05/2009, 22:55
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Función translate en mysql

Tenias razón gnzsoloyo

El rendimiento se ve seriamente afectado en un numero excesivo de registros.

hice pruebas de rendimiento en una tabla con 240000 registros, comparando el rendimiento de
las consultas haciendo uso de la función y sin hacer uso de la función.

Para 10000 registros
Con función: 10 segundos.
Sin función: 07 segundos.

Para 50000 registros
Con función: 55 segundos.
Sin función: 34 segundos.

Para 100000 registros
Con función: 106 segundos.
Sin función: 068 segundos.

Para 200000 registros
Con función: 212 segundos.
Sin función: 135 segundos.

En conclusión, Para consultas con excesivo numero de registros, la función queda obsoleta, sin embargo, para consultas que no superen los 50000 registros, me parece que es un tiempo considerable de espera.

un saludo y espero alguien la use, a mi me va perfecto.
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #9 (permalink)  
Antiguo 29/05/2009, 02:10
Colaborador
 
Fecha de Ingreso: marzo-2008
Ubicación: Cáceres
Mensajes: 3.735
Antigüedad: 16 años
Puntos: 300
Respuesta: Función translate en mysql

amigo huesos52,
yo soy de la opinión de gnzsoloyo.
dices
Cita:
Es posible que hace falta algo pequeño
Creo que lo que falta es que la base no tiene la codificación adecuada; sí las tablas, pero no la base.
Crea una base nueva con codificación latin1, y collation latin1_spanish_ci; y dentro de ella, crea tu tabla tal y como la tienes. Luego haz las búsquedas. Yo lo he probado y funciona perfectamente.
Creo que casi todos los problemas de este tipo se resuelven con la codificación, el charset; el problema es que (y lo digo por mí) creamos bases sin estudiar nuestras necesidades futuras y sin pensar en la mejor codificación, y aceptamos la codificación y collation por defecto: la famosa latin1_swedish_ci, pero ninguno hacemos las comparaciones en sueco, que yo sepa. Piensa que cuando eliges eso en el PHPMyAdmin estás eligiendo la collation latin1_swedish_ci, y el charset latin1 (el equivalente al ISO-8859-1) para la base y para las conexiones. Aunque luego codifiques las tablas bien (imagenemos que quieres utf8), ya podrías, creo, tener problemas.
Intervienen otros factores sobre la codificación, como el hecho de la codificación que se envía a través del navegador al servidor y cómo la devuelve (las conexiones). Los archivos de programación y los de html tienen mucho que ver también, e incluso si se pasa un dato por el método POST o GET, tendrá cierta influencia sobre la codificación cuando pase por el navegador, al menos eso he leído.

Última edición por jurena; 29/05/2009 a las 03:38
  #10 (permalink)  
Antiguo 29/05/2009, 05:27
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Función translate en mysql

jejeje...

Jurena... creeme, yo también soy de la opinión de gnzsoloyo. Solucionar estos problemas adecuadamente, es la mejor opción que puede existir a dicho problema de acentuaciones.

En el pasado, he tenido estos problemas, y los he solucionado con opiniones tuyas (en gran mayoría) de los foros y todo lo que recomiendas como tal.
Cita:
el problema es que (y lo digo por mí) creamos bases sin estudiar nuestras necesidades futuras y sin pensar en la mejor codificación
Creeme, esto nos pasa a todos. Son pocas las veces que pensamos en esto, antes de
realizar un correcto diseño de las bases de datos.

He notado que en todos los post que se relacionan con este problema, la solución se da despues del mensaje 20 y la opinión de unos cuantos cristianos jejeje.

La verdad, me causó mucha intriga que este problema en oracle, la solución que mas recomiendan es el uso de la función translate. El objetivo de esta función, no es asegurar que es mejor usar este método para solucionar el problema, simplemente tiene como objetivo facilitar a algunos usuarios que no tenemos dominio en el tema de charset y collation para implementar una solución alternativa. Por que aceptemoslo, es un tema bien maluco.

Un saludo a todos y gracias por sus comentarios.
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #11 (permalink)  
Antiguo 29/05/2009, 06:57
Colaborador
 
Fecha de Ingreso: marzo-2008
Ubicación: Cáceres
Mensajes: 3.735
Antigüedad: 16 años
Puntos: 300
Respuesta: Función translate en mysql

Estoy de acuerdo contigo, huesos52, se trata de "un tema bien maluco" (permíteme que emplee tu expresión: me ha gustado), principalmente porque asignamos al azar las codificaciones, y luego porque cuando almacenamos algo en una codificación queremos que pulsando un botón de cambio se resuelva todo: y eso no es así. Nos preocupa tener que darle la vuelta a la codificación y no queremos dedicar ni un minuto a hacer ese esfuerzo, máxime cuando el problema de codificación afecta no sólo a la base.
Saludos y gracias a ti y a todos los que nos formáis cada día en este foro.
  #12 (permalink)  
Antiguo 29/05/2009, 10:33
Avatar de Genetix  
Fecha de Ingreso: febrero-2002
Ubicación: Lima - Perú
Mensajes: 1.600
Antigüedad: 22 años, 2 meses
Puntos: 45
Respuesta: Función translate en mysql

Hola
Como bien dicen es un dolor de cabeza, por lo general es recomendable (o por lo menos eso es mi premisa) de usar todo en utf-8 que es lo mas universal que existe, y evita muchos dolores de cabeza.

Pero hay casos en que la data puede estar determinada en otros tipos de codificación, para eso suelo usar el set names con el cual se puede jugar con lo que se envía y recibe del servidor sin tocar las tablas ni la DB.

ejemplo
Código sql:
Ver original
  1. mysql> SET NAMES UTF8;
  2. Query OK, 0 ROWS affected (0.00 sec)
  3.  
  4. mysql> SELECT *
  5.     -> FROM nombres n
  6.     -> WHERE NOMBRE LIKE'%MARIA%';
  7.  
  8. +----------------+
  9. | NOMBRE         |
  10. +----------------+
  11. | MARIA LUCÍA    |
  12. | MARÍA          |
  13. | MARÍA FERNANDA |
  14. +----------------+
  15. 3 ROWS IN SET (0.00 sec)

Hay mas comandos que se pueden usar
http://dev.mysql.com/doc/refman/5.0/...onnection.html
Para manipular lo que se envía y recibe.

Saludos!
__________________
"El conocimiento nos hace responsables."
twitter: @benjamingb
blog personal: http://codigolinea.com
ZF Manual en español http://manual.zfdes.com
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 22:53.