2011-12-15 74 views
13

Digamos que tengo un conjunto de 2 palabras:T-SQL Obtener porcentaje de coincidencia carácter de 2 cadenas

Alexander y Alecsander O Alexander y Alegzander

Alexander y Aleaxnder, o cualquier otra combinación. En general, estamos hablando de errores humanos al escribir una palabra o un conjunto de palabras.

Lo que quiero lograr es obtener el porcentaje de coincidencia de los caracteres de las 2 cuerdas.

Esto es lo que tengo hasta ahora:

DECLARE @table1 TABLE 
(
    nr INT 
    , ch CHAR 
) 

DECLARE @table2 TABLE 
(
    nr INT 
    , ch CHAR 
) 


INSERT INTO @table1 
SELECT nr,ch FROM [dbo].[SplitStringIntoCharacters] ('WORD w') --> return a table of characters(spaces included) 

INSERT INTO @table2 
SELECT nr,ch FROM [dbo].[SplitStringIntoCharacters] ('WORD 5') 

DECLARE @resultsTable TABLE 
( 
ch1 CHAR 
, ch2 CHAR 
) 
INSERT INTO @resultsTable 
SELECT DISTINCt t1.ch ch1, t2.ch ch2 FROM @table1 t1 
FULL JOIN @table2 t2 ON t1.ch = t2.ch --> returns both matches and missmatches 

SELECT * FROM @resultsTable 
DECLARE @nrOfMathches INT, @nrOfMismatches INT, @nrOfRowsInResultsTable INT 
SELECT @nrOfMathches = COUNT(1) FROM @resultsTable WHERE ch1 IS NOT NULL AND ch2 IS NOT NULL 
SELECT @nrOfMismatches = COUNT(1) FROM @resultsTable WHERE ch1 IS NULL OR ch2 IS NULL 


SELECT @nrOfRowsInResultsTable = COUNT(1) FROM @resultsTable 


SELECT @nrOfMathches * 100/@nrOfRowsInResultsTable 

El SELECT * FROM @resultsTable volverán los siguientes:

ch1   ch2 
NULL  5 
[blank]  [blank] 
D   D 
O   O 
R   R 
W   W 
+0

¿Y cuál es el problema con esto? ¿Ese código funciona correctamente? –

+0

Eso no es preciso. –

Respuesta

20

Ok, aquí está mi solución hasta el momento:

SELECT [dbo].[GetPercentageOfTwoStringMatching]('valentin123456' ,'valnetin123456') 

retornos 86%

CREATE FUNCTION [dbo].[GetPercentageOfTwoStringMatching] 
(
    @string1 NVARCHAR(100) 
    ,@string2 NVARCHAR(100) 
) 
RETURNS INT 
AS 
BEGIN 

    DECLARE @levenShteinNumber INT 

    DECLARE @string1Length INT = LEN(@string1) 
    , @string2Length INT = LEN(@string2) 
    DECLARE @maxLengthNumber INT = CASE WHEN @string1Length > @string2Length THEN @string1Length ELSE @string2Length END 

    SELECT @levenShteinNumber = [dbo].[LEVENSHTEIN] ( @string1 ,@string2) 

    DECLARE @percentageOfBadCharacters INT = @levenShteinNumber * 100/@maxLengthNumber 

    DECLARE @percentageOfGoodCharacters INT = 100 - @percentageOfBadCharacters 

    -- Return the result of the function 
    RETURN @percentageOfGoodCharacters 

END 




-- =============================================  
-- Create date: 2011.12.14 
-- Description: http://blog.sendreallybigfiles.com/2009/06/improved-t-sql-levenshtein-distance.html 
-- ============================================= 

CREATE FUNCTION [dbo].[LEVENSHTEIN](@left VARCHAR(100), 
            @right VARCHAR(100)) 
returns INT 
AS 
    BEGIN 
     DECLARE @difference INT, 
       @lenRight  INT, 
       @lenLeft  INT, 
       @leftIndex  INT, 
       @rightIndex INT, 
       @left_char  CHAR(1), 
       @right_char CHAR(1), 
       @compareLength INT 

     SET @lenLeft = LEN(@left) 
     SET @lenRight = LEN(@right) 
     SET @difference = 0 

     IF @lenLeft = 0 
     BEGIN 
      SET @difference = @lenRight 

      GOTO done 
     END 

     IF @lenRight = 0 
     BEGIN 
      SET @difference = @lenLeft 

      GOTO done 
     END 

     GOTO comparison 

     COMPARISON: 

     IF (@lenLeft >= @lenRight) 
     SET @compareLength = @lenLeft 
     ELSE 
     SET @compareLength = @lenRight 

     SET @rightIndex = 1 
     SET @leftIndex = 1 

     WHILE @leftIndex <= @compareLength 
     BEGIN 
      SET @left_char = substring(@left, @leftIndex, 1) 
      SET @right_char = substring(@right, @rightIndex, 1) 

      IF @left_char <> @right_char 
       BEGIN -- Would an insertion make them re-align? 
        IF(@left_char = substring(@right, @rightIndex + 1, 1)) 
        SET @rightIndex = @rightIndex + 1 
        -- Would an deletion make them re-align? 
        ELSE IF(substring(@left, @leftIndex + 1, 1) = @right_char) 
        SET @leftIndex = @leftIndex + 1 

        SET @difference = @difference + 1 
       END 

      SET @leftIndex = @leftIndex + 1 
      SET @rightIndex = @rightIndex + 1 
     END 

     GOTO done 

     DONE: 

     RETURN @difference 
    END 
+0

por lo que ha publicado una pregunta en vano ^^ –

+12

@aF. No, no publiqué una pregunta para nada.Publiqué una pregunta, luego continué buscando una solución a mi problema. Encontré algo útil y lo publiqué aquí, así que tal vez alguien con más conocimiento que yo pueda decirme si hay una forma mejor o más precisa de lograrlo. Además, en el futuro tal vez alguien se beneficiará de esto. He tenido otras situaciones similares (http://stackoverflow.com/questions/3107514/html-agility-pack-strip-tags-not-in-whitelist) donde mis respuestas han ayudado a otros. –

+0

+1 para acreditar la fuente de su algoritmo de distancia Levenshtein en los comentarios del código. De buen tono. –

8

En última instancia, parece que está buscando resolver la probabilidad de que dos cadenas sean una coincidencia "confusa" entre sí.

SQL proporciona funciones incorporadas eficientes y optimizadas que lo harán por usted, y probablemente con un mejor rendimiento que el que ha escrito. Las dos funciones que busca son SOUNDEX y DIFFERENCE.

Si bien ninguno de ellos resuelve exactamente lo que usted solicitó, es decir, no devuelven una coincidencia porcentual, creo que resuelven lo que finalmente está tratando de lograr.

SOUNDEX devuelve un código de 4 caracteres que es la primera letra de la palabra más un código de 3 números que representa el patrón de sonido de la palabra. Considere lo siguiente:

SELECT SOUNDEX('Alexander') 
SELECT SOUNDEX('Alegzander') 
SELECT SOUNDEX('Owleksanndurr') 
SELECT SOUNDEX('Ulikkksonnnderrr') 
SELECT SOUNDEX('Jones') 

/* Results: 

A425 
A425 
O425 
U425 
J520 

*/ 

Lo que va a notar es que el número de tres dígitos 425 es el mismo para todos los que suenan igual o menos. Entonces, podrías emparejarlos fácilmente y decir "Escribiste 'Owleksanndurr', ¿quizás quisiste decir 'Alexander'?"

Además, está la función DIFFERENCE, que compara la discrepancia SOUNDEX entre dos cadenas y le da una puntuación.

SELECT DIFFERENCE( 'Alexander','Alexsander') 
SELECT DIFFERENCE( 'Alexander','Owleksanndurr') 
SELECT DIFFERENCE( 'Alexander', 'Jones') 
SELECT DIFFERENCE( 'Alexander','ekdfgaskfalsdfkljasdfl;jl;asdj;a') 

/* Results: 

4 
3 
1 
1  

*/ 

Como se puede ver, cuanto menor es la puntuación (entre 0 y 4), los más probables las cadenas son un partido.

La ventaja de SOUNDEX sobre DIFFERENCE es que si realmente necesita hacer coincidencia aproximada frecuente, puede almacenar e indexar los datos SOUNDEX en una columna separada (indexable), mientras que DIFFERENCE sólo se puede calcular el SOUNDEX en el momento de la comparación .

+0

+1 Gracias. Tomaré en consideración tu respuesta. A simple vista, solo puedo tomar en consideración los resultados que tienen "1". –

+0

Una coincidencia exacta devolverá cero. No lo mostré en el ejemplo, pero es importante saberlo para que no empieces a DONDE DIFERENCIA (...) = 1 y te pierdas todas las coincidencias perfectas. :) –

+4

En realidad, 0 indica una similitud débil o nula y 4 indica una gran similitud. http://msdn.microsoft.com/en-us/library/ms188753.aspx – Auresco82

Cuestiones relacionadas