En el trabajo diario como desarrollador de aplicaciones, es común llegar a necesitar extender la funcionalidad existente en tu manejador de bases de datos para que en tus cadenas sql puedas usar tal funcionalidad como si fuera nativa del manejador.

Me explico mejor con un ejemplo: Supongamos que requiero calcular la “distancia” entre dos palabras con la métrica de levenshtein y esto deseo calcularlo para cada túpla en mi tabla “Personas” aplicado a los atributos apPat y apMat. Mi cadena sql luciría mas o menos así:

SELECT id, apPat, apMat, levenshtein(apPat, apMat) as distancia FROM Personas;

Arrojando un resultado como el siguiente:

+—-+———+———-+———–+

| id | apPat   | apMat    | distancia |

+—-+———+———-+———–+

| 1 | Arellano | Arellani | 1         |

| 2 | Arevalo  | Orevale  | 2         |

| 3 | Benitez  | Venitez  | 2         |

| 4 | Cardona  | Cardona  | 0         |

| 5 | Corona   | Lopez    | 6         |

+—-+———+———-+———–+

5 rows in set (0.07 sec)

Pero, cómo lograrlo?

Bueno, encontré dos formas. Una fácil, pero con cierto castigo en el desempeño de la ejecución de la consulta. Y uno difícil (y que NO sé cómo llevar a cabo en Windows, sólo lo efectué en LINUX) pero con un desempeño excelente.

Primero, el primero:

Desde tu consola de MySQL y habiendo ya creado y seleccionado la base de datos ( y la tabla “Personas” con algunos datos de prueba) llevar a cabo lo siguiente:

mysql> delimiter //

mysql> CREATE FUNCTION levenshtein( s1 VARCHAR(255), s2 VARCHAR(255) )
  RETURNS INT
  DETERMINISTIC
  BEGIN
    DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT;
    DECLARE s1_char CHAR;
    -- max strlen=255
    DECLARE cv0, cv1 VARBINARY(256);
    SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), cv1 = 0x00, j = 1, i = 1, c = 0;
    IF s1 = s2 THEN
      RETURN 0;
    ELSEIF s1_len = 0 THEN
      RETURN s2_len;
    ELSEIF s2_len = 0 THEN
      RETURN s1_len;
    ELSE
      WHILE j <= s2_len DO
        SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1;
      END WHILE;
      WHILE i <= s1_len DO
        SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1;
        WHILE j <= s2_len DO
          SET c = c + 1;
          IF s1_char = SUBSTRING(s2, j, 1) THEN 
            SET cost = 0; ELSE SET cost = 1;
          END IF;
          SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost;
          IF c > c_temp THEN SET c = c_temp; END IF;
            SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1;
            IF c > c_temp THEN 
              SET c = c_temp; 
            END IF;
            SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1;
        END WHILE;
        SET cv1 = cv0, i = i + 1;
      END WHILE;
    END IF;
    RETURN c;
  END;

//

 

 

mysql> delimiter ;

Ahora ya podemos ejecutar el query que mencionaba originalmente, obteniendo el resultado descrito, y hasta queries como:

SELECT * from Personas where levenshtein(apPat, apMat)<2;

PARTE II

La forma “difícil” es usando C, compilando el código ya declarándolo en mySQL. Va más o menos así:

Con el código en C (que puedes obtener del tar de la liga siguiente, o puedes pedírmelo y lo desempolvo de mi privado/software/tars ) hice lo siguiente:

%> sudo yum install mysqld gcc-c++

%> wget http://joshdrew.com/mysql_levenshtein_udf-1.0.tar.gz

%> tar zxf mysql_levenshtein_udf-1.0.tar.gz

%> gcc -shared -o mysqllevenshtein.so mysqllevenshtein.cc -I/usr/include/mysql/

%> sudo cp mysqllevenshtein.so /usr/lib/

y en MySQL:

mysql> use miBaseDeDatos;

mysql> CREATE FUNCTION levenshtein RETURNS INT SONAME ‘mysqllevenshtein.so’;

Y ya quedó. El desempeño es 100% mejor, ya que baja de 0.07 a 0.00. (La verdad, no es un 0 absoluto, pero con ello puedo ver que tardó menos de 0.00 y con eso, es suficiente para mi.)

Hasta pronto!

Goose

Maldito Telcel. Ya estoy harto de que me roben y me traten de ver la cara de idiota con sus promociones estúpidas. De por sí el minuto (o fracción, que por cierto me parece un robo dentro de un robo) es caro y si hablas un minuto con un segundo, ya te cobran 2.

Por otro lado, las promociones que eran medio buenas (como el plan combinado) las eliminaron, para que el cliente gastara mas. Ahora veo que el famoso “Saldo de regalo” es otra tomada de pelo, porque caduca y no lo puedo transferir a otro teléfono. Tampoco puedo comprar tiempo de Internet con ese “regalo”. Sólo sirve para hablar y mandar mensajes de texto A USUARIOS TELCEL.

Que idiotez!

Hay dos aspectos que casi me provocan el vómito: Las comunicaciones no son muy buenas, esto con independencia del teléfono que uses y el servicio de “banda ancha” es un asco! Súper lento y caro.

Ojalá que pronto aparezca un competidor digno del tirano Telcel y le haga la vida de cuadritos (con promociones reales para sus clientes) y así pueda ver (Telcel) que no es buena idea actuar de la forma en la que lo ha venido haciendo desde hace mas de diez años.

© 2017 Goose Workshop Suffusion theme by Sayontan Sinha