2011-12-15 6 views
13

Diciamo che ho un set di 2 parole:T-SQL Get percentuale di partita carattere di 2 stringhe

Alexander e Alecsander OR Alessandro e Alegzander

Alessandro e Aleaxnder, o qualsiasi altra combinazione. In generale stiamo parlando di errore umano nella digitazione di una parola o di un insieme di parole.

Quello che voglio ottenere è ottenere la percentuale di corrispondenza dei caratteri delle 2 stringhe.

Ecco quello che ho finora:

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 

Il SELECT * FROM @resultsTable restituirà il seguente:

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

E qual è il problema? Il codice funziona correttamente? –

+0

Che non è preciso. –

risposta

20

Ok, ecco la mia soluzione finora:

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

rendimenti 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

così hai postato una domanda per niente ^^ –

+12

@aF. No, non ho posto una domanda per niente.Ho postato una domanda, poi ho continuato a cercare una soluzione al mio problema. Ho trovato qualcosa di utile e l'ho pubblicato qui, quindi forse qualcuno con più conoscenze di me può dirmi se c'è un modo migliore o più preciso per farlo. Inoltre, in futuro forse qualcuno ne trarrà beneficio. Ho avuto altre situazioni simili (http://stackoverflow.com/questions/3107514/html-agility-pack-strip-tags-not-in-whitelist) dove le mie risposte hanno aiutato gli altri. –

+0

+1 per accreditare la fonte del tuo algoritmo di distanza Levenshtein nei commenti del codice. Classy. –

8

In definitiva, sembra che tu stia cercando di risolvere la probabilità che due stringhe siano una "sfocata" l'una rispetto all'altra.

SQL fornisce funzioni integrate efficienti e ottimizzate che lo faranno per te e probabilmente con prestazioni migliori di quelle che hai scritto. Le due funzioni che stai cercando sono SOUNDEX e DIFFERENCE.

Mentre nessuno dei due risolve esattamente quello che hai chiesto - cioè non restituiscono una corrispondenza percentuale - credo che risolvano ciò che alla fine stai cercando di ottenere.

SOUNDEX restituisce un codice di 4 caratteri che è la prima lettera della parola più un codice di 3 numeri che rappresenta il modello sonoro della parola. Si consideri il seguente:

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

/* Results: 

A425 
A425 
O425 
U425 
J520 

*/ 

cosa che si nota è che il numero di tre cifre 425 è lo stesso per tutti quelli che suonano più o meno allo stesso modo. Quindi potresti facilmente abbinarli e dire "Hai digitato 'Owleksanndurr', intendevi forse 'Alexander'?"

Inoltre, c'è la funzione DIFFERENCE, che confronta la discrepanza SOUNDEX tra due stringhe e fornisce un punteggio.

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

/* Results: 

4 
3 
1 
1  

*/ 

Come si può vedere, il basso è il punteggio (da 0 a 4), più è probabile che le stringhe sono un fiammifero.

Il vantaggio di SOUNDEX sopra DIFFERENCE è che se si ha realmente bisogno di fare frequenti corrispondenza fuzzy, è possibile memorizzare e indicizzare i dati SOUNDEX in una colonna separata (indicizzabili), mentre DIFFERENCE può calcolare solo la SOUNDEX al momento di paragone .

+0

+1 Grazie. Prenderò in considerazione la tua risposta. Dagli sguardi, posso solo prendere in considerazione i risultati che hanno "1". –

+0

Una corrispondenza esatta restituirà zero. Nell'esempio non l'ho mostrato, ma è importante sapere che non inizi a fare WHERE DIFFERENCE (...) = 1 e manchi tutte le corrispondenze perfette. :) –

+4

In realtà, 0 indica debole o nessuna somiglianza e 4 indica forte somiglianza. http://msdn.microsoft.com/en-us/library/ms188753.aspx – Auresco82

Problemi correlati