domingo, 17 de julio de 2011

Comparaciones fonéticas utilizando el Metaphone

En SQL Server existen dos funciones que sirven para realizar comparaciones fonéticas de nombres o palabras de acuerdo al sonido que emiten. Estas funciones llamadas SOUNDEX y DIFFERENCES provienen del algoritmo fonético Soundex, desarrollado por Robert Russell y Margaret Odell, que indexa los nombres de acuerdo a su sonido en inglés.

En SQL la función Soundex genera el sonido que arrojar una palabra, mientras que Differences realiza una comparación entre los sonidos de dos palabras a la que se le aplica el Soundex internamente.

El Soundex tiene ciertas deficiencias, que no lo hace tan efectivo, por ejemplo, las consonantes que suenan parecido devuelven el mismo valor (B, F y P devuelven 1) y las vocales pueden afectar la codificación, sin embargo es poco utilizable.

Por esta razón, Lawrence Phillips desarrolla el algoritmo Metaphone en respuesta a las deficiencias que presentaba el primero. Este fue publicado en 1990 y se encuentra publicado en distintos lenguajes, C, PHP, SQL, etc para indexar palabras por su sonido en el idioma inglés.

Este ya viene integrado en PHP, esperamos que pronto sea integrado al SQL Server como se hizo con el Soundex, para así ayudar un poco a los desarrolladores. El autor más tarde desarrolló una nueva versión del algoritmo, al que llamó "Double Metaphone", que produce resultados más exactos que el original

Debajo el ejemplo del código MetaPhone, para su uso recomiendo crearlo en la base de datos master, para que el mismo pueda ser implementado en llamado desde cualquier base de datos:

USE [master]
GO
/****** Object:  UserDefinedFunction [dbo].[Metaphone]    Script Date: 06/30/2011 11:57:07 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[Metaphone](@str as varchar(70))
RETURNS VARCHAR (25)
      /*
      Metaphone Algorithm
     
      Created by Lawrence Philips.
      Metaphone presented in article in "Computer Language" December 1990 issue.
      Translated into t-SQL by Keith Henry (keithh_AT_lbm-solutions.com)
     
                   *********** BEGIN METAPHONE RULES ***********
       Lawrence Philips' RULES follow:
       The 16 consonant sounds:
                                                   |--- ZERO represents "th"
                                                   |
            B  X  S  K  J  T  F  H  L  M  N  P  R  0  W  Y
       Drop vowels
     
       Exceptions:
         Beginning of word: "ae-", "gn", "kn-", "pn-", "wr-"   ----> drop first letter
         Beginning of word: "x"                                ----> change to "s"
         Beginning of word: "wh-"                              ----> change to "w"
         Beginning of word: vowel                              ----> Keep it
     
     
      Transformations:
         B ----> B      unless at the end of word after "m", as in "dumb", "McComb"
     
         C ----> X      (sh) if "-cia-" or "-ch-"
                 S      if "-ci-", "-ce-", or "-cy-"
                        SILENT if "-sci-", "-sce-", or "-scy-"
                 K      otherwise, including in "-sch-"
     
         D ----> J      if in "-dge-", "-dgy-", or "-dgi-"
                 T      otherwise
     
         F ----> F
     
         G ---->        SILENT if in "-gh-" and not at end or before a vowel
                                  in "-gn" or "-gned"
                                  in "-dge-" etc., as in above rule
                 J      if before "i", or "e", or "y" if not double "gg"
                 K      otherwise
     
         H ---->        SILENT if after vowel and no vowel follows
                               or after "-ch-", "-sh-", "-ph-", "-th-", "-gh-"
                 H      otherwise
     
         J ----> J
     
         K ---->        SILENT if after "c"
                 K      otherwise
     
         L ----> L
     
         M ----> M
     
         N ----> N
     
         P ----> F      if before "h"
                 P      otherwise
     
         Q ----> K
     
         R ----> R
     
         S ----> X      (sh) if before "h" or in "-sio-" or "-sia-"
                 S      otherwise
     
         T ----> X      (sh) if "-tia-" or "-tio-"
                 0      (th) if before "h"
                        silent if in "-tch-"
                 T      otherwise
     
         V ----> F
     
         W ---->        SILENT if not followed by a vowel
                 W      if followed by a vowel
     
         X ----> KS
     
         Y ---->        SILENT if not followed by a vowel
                 Y      if followed by a vowel
     
         Z ----> S
      */


AS
BEGIN
DECLARE     @Result VARCHAR(25),
  @str3 CHAR(3),
  @str2 CHAR(2),
  @str1 CHAR(1),
  @strp CHAR(1),
  @strLen TINYINT,
  @cnt TINYINT

SET @strLen = LEN(@str)
SET @cnt = 1
SET @Result = ''

--Process beginning exceptions
set   @str2       =     left(@str,2)
if    @str2       in    ('ae', 'gn', 'kn', 'pn', 'wr')
      begin
            set   @str =     right(@str , @strLen - 1)
            set   @strLen =   @strLen - 1
      end
if    @str2       =     'wh' 
      begin
            set   @str =     'w' + right(@str , @strLen - 2)
            set   @strLen =   @strLen - 1
      end
set   @str1       =     left(@str,1)
if    @str1 =     'x'  
      begin
            set   @str =     's' + right(@str , @strLen - 1)
      end
if    @str1 in    ('a','e','i','o','u')
      begin
            set   @str =     right(@str , @strLen - 1)
            set   @strLen =   @strLen - 1
            set   @Result     =     @str1
      end

while @cnt <= @strLen
      begin
            set   @str1       =     substring(@str,@cnt,1)
            if    @cnt <>    1    
                  set   @strp =     substring(@str,(@cnt-1),1)
            else  set   @strp =     ' '
     
      if @strp    <>    @str1
            begin
                  set   @str2       =     substring(@str,@cnt,2)
                       
                  if    @str1 in    ('f','j','l','m','n','r')    
                        set   @Result     =     @Result + @str1
     
                  if    @str1 =     'q'   set @Result =     @Result + 'k'
                  if    @str1 =     'v'   set @Result =     @Result + 'f'
                  if    @str1 =     'x'   set @Result =     @Result + 'ks'
                  if    @str1 =     'z'   set @Result =     @Result + 's'
     
                  if    @str1 =     'b'
                        if @cnt = @strLen
                              if    substring(@str,(@cnt - 1),1) <> 'm'
                                    set   @Result     =     @Result + 'b'
                        else
                              set   @Result     =     @Result + 'b'
     
                  if    @str1 =     'c'

                        if    @str2       =     'ch' or    substring(@str,@cnt,3) =       'cia'
                              set   @Result     =     @Result + 'x'
                        else
                              if    @str2 in    ('ci','ce','cy')  and   @strp <>    's'
                                    set   @Result     =     @Result + 's'
                              else  set   @Result     =     @Result + 'k'
     
                  if    @str1 =     'd'
                        if    substring(@str,@cnt,3) in    ('dge','dgy','dgi')
                              set   @Result     =     @Result + 'j'
                        else  set   @Result     =     @Result + 't'
     
                  if    @str1 =     'g'
                        if    substring(@str,(@cnt - 1),3) not in ('dge','dgy','dgi','dha','dhe','dhi','dho','dhu')
                              if  @str2 in ('gi', 'ge','gy')
                                    set   @Result     =     @Result + 'j'
                              else
                                    if    (@str2      <>    'gn') or ((@str2  <> 'gh') and ((@cnt + 1) <> @strLen))
                                          set   @Result     =     @Result + 'k'
     
                  if    @str1 =     'h'
                        if    (@strp not in ('a','e','i','o','u')) and (@str2 not in ('ha','he','hi','ho','hu'))
                              if    @strp not in ('c','s','p','t','g')
                                    set   @Result     =     @Result + 'h'
     
                  if    @str1 =     'k'  
                        if @strp <> 'c'
                              set   @Result     =     @Result + 'K'
     
                  if    @str1 =     'p'  
                        if @str2 = 'ph'
                              set   @Result     =     @Result + 'f'
                        else
                              set   @Result     =     @Result + 'p'
     
                  if    @str1 =     's'
                        if    substring(@str,@cnt,3) in    ('sia','sio') or @str2 = 'sh'
                              set   @Result     =     @Result + 'x'
                        else  set   @Result     =     @Result + 's'
     
                  if    @str1 =     't'
                        if    substring(@str,@cnt,3) in    ('tia','tio')
                              set   @Result     =     @Result + 'x'
                        else 
                              if    @str2 =     'th'
                                    set   @Result     =     @Result + '0'
                              else
                                    if substring(@str,@cnt,3) <> 'tch'
                                          set   @Result     =     @Result + 't'
     
                  if    @str1 =     'w'
                        if @str2 not in('wa','we','wi','wo','wu')
                              set   @Result     =     @Result + 'w'
     
                  if    @str1 =     'y'
                        if @str2 not in('ya','ye','yi','yo','yu')
                              set   @Result     =     @Result + 'y'
            end
            set   @cnt  =     @cnt + 1
      end
      RETURN UPPER(@Result)
END

Enlaces:

1 comentario:

El Rey Hidrógeno dijo...

El Gurú,

La fonetización en español, interesante tema cierto?

SoundEsp ya esta listo y casi en linea para pruebas; Fonetizador en español.

http://soundesp.somee.com/Fonetizador.asp

Saludos.