2011-06-27 16 views
6

¿Cuál es la mejor manera de clasificar una columna varchar sql por el número (conteo)/coincidencia de palabras en un parámetro, con cuatro criterios únicos distintos. Probablemente esta no sea una pregunta trivial, pero tengo el desafío de ordenar las filas según la "mejor coincidencia" utilizando mis criterios.El mejor método de SQL Server para hacer coincidir frases de palabras y orden relevence

columna: Descripción varchar (100) Parámetro: varchar @MyParameter (100)

de salida con este orden de preferencia:

  • La concordancia exacta (toda cadena coincide) - siempre en primer lugar
  • Comienza con (descendente basado en la longitud del parámetro de la coincidencia)
  • Recuento de palabras rango con palabras contiguas superior para el mismo recuento de palabras que coinciden
  • Palabra (s) partido en cualquier lugar (no contiguas)

palabras no coincidir exactamente, coincidencias parciales de una palabra se permite y es probable, el valor arrendador deben aplicarse a las palabras parciales para la clasificación pero no crítica (olla haría haga coincidir cada uno de ellos en: olla, alfarero, potholder, depósito, depósito, por ejemplo). Comienza con otras coincidencias de palabras que deben ser más altas que las que no tienen coincidencias posteriores, pero eso no es un asesino de reparto/súper importante.

Me gustaría tener un método para clasificar donde la columna "comienza con" el valor en el parámetro. Decir que tengo la siguiente cadena:

'This is my value string as a test template to rank on.' 

me gustaría tener, en el primer caso una fila de la columna/fila donde existe el mayor número de palabras.

Y el segundo para clasificar basan en ocurrencia (ideal) en el comienzo como:

'This is my string as a test template to rank on.' - first 
'This is my string as a test template to rank on even though not exact.'-second 
'This is my string as a test template to rank' - third 
'This is my string as a test template to' - next 
'This is my string as a test template' - next etc. 

En segundo lugar: (posiblemente segundo set/grupo de datos después de la primera (comienza con) - Esto es deseable

Quiero clasificar (más o menos) las filas por el conteo de palabras en el @MyParameter que se producen en @MyParameter con un rango donde las palabras contiguas rango más alto que en el mismo recuento por separado.

Así, por ejemplo, la cadena anterior , 'is my string as shown' woul d rango más alto que 'is not my other string as' debido a la "mejor coincidencia" de la cadena contigua (palabras juntas) con el mismo recuento de palabras. Las filas con una coincidencia más alta (recuento de palabras que se producen) clasificarían primero como la mejor combinación descendente.

Si es posible, me gustaría hacer esto en una sola consulta.

No debería aparecer ninguna fila dos veces en el resultado.

Para consideraciones de rendimiento, no se producirán más de 10.000 filas en la tabla.

Los valores en la tabla son bastante estáticos con pocos cambios pero no totalmente.

No puedo cambiar la estructura en este momento, pero lo consideraría más tarde (como una tabla de palabras/frases)

Para que esto sea un poco más complicada, la lista de palabras es en dos mesas - pero podría crear una vista para eso, pero los resultados de una tabla (lista más pequeña) deben ocurrir antes de un segundo resultado de conjunto de datos más grande con la misma coincidencia: habrá duplicados de estas tablas y dentro de una tabla, y solo quiero valores distintos. Seleccionar DISTINCT no es fácil ya que quiero devolver una columna (sourceTable) que podría hacer que las filas sean distintas y en ese caso solo seleccionar de la primera (más pequeña) tabla, pero todas las otras columnas DISTINCT son deseadas (no considere que columna en la evaluación "distinta"

columnas Psuedo en la tabla:.

procedureCode VARCHAR(50), 
description VARCHAR(100), -- this is the sort/evaluation column 
category VARCHAR(50), 
relvu  VARCHAR(50), 
charge VARCHAR(15), 
active bit 
sourceTable VARCHAR(50) - just shows which table it comes from of the two 

índice NO única existe como una columna de ID

Partidos NO en una tercera tabla que ser excluidos SELECT * FROM (select * from tableone where procedureCode not in (select procedureCode from tablethree)) UNION ALL (select * from tabletwo where procedureCode not in (select procedureCode from tablethree))

EDIT: en un intento de hacer frente a esto he creado un parametro valor de la tabla de este modo:

0  Gastric Intubation & Aspiration/Lavage, Treatmen 
1  Gastric%Intubation%Aspiration%Lavage%Treatmen 
2  Gastric%Intubation%Aspiration%Lavage 
3  Gastric%Intubation%Aspiration 
4  Gastric%Intubation 
5  Gastric 
6  Intubation%Aspiration%Lavage%Treatmen 
7  Intubation%Aspiration%Lavage 
8  Intubation%Aspiration 
9  Intubation 
10  Aspiration%Lavage%Treatmen 
11  Aspiration%Lavage 
12  Aspiration 
13  Lavage%Treatmen 
14  Lavage 
15  Treatmen 

donde la frase real se encuentra en la fila 0

Aquí está mi intento actual de esto;

CREATE PROCEDURE [GetProcedureByDescription] 
( 
     @IncludeMaster BIT, 
     @ProcedureSearchPhrases CPTFavorite READONLY 

) 
AS 

    DECLARE @myIncludeMaster BIT; 

    SET @myIncludeMaster = @IncludeMaster; 

    CREATE TABLE #DistinctMatchingCpts 
    (
    procedureCode VARCHAR(50), 
    description  VARCHAR(100), 
    category  VARCHAR(50), 
    rvu  VARCHAR(50), 
    charge  VARCHAR(15), 
    active  VARCHAR(15), 
    sourceTable VARCHAR(50), 
    sequenceSet VARCHAR(2) 
    ) 

    IF @myIncludeMaster = 0 
     BEGIN -- Excluding master from search 
      INSERT INTO #DistinctMatchingCpts (sourceTable, procedureCode, description , category ,charge, active, rvu, sequenceSet 
) 
     SELECT DISTINCT sourceTable, procedureCode, description, category ,charge, active, rvu, sequenceSet 
      FROM (
        SELECT TOP 1 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[COMBO])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         ''True'' AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''0CPTMore'' AS sourceTable, 
         ''01'' AS sequenceSet 
        FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [CPTMORE] AS CPT 
         ON CPT.[LEVEL] = PP.[LEVEL] 
        WHERE 
         (CPT.[COMBO] IS NULL OR CPT.[COMBO] NOT IN (''Editor'',''MOD'',''CATEGORY'',''Types'',''Bundles'')) 
         AND CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 
        ORDER BY PP.CODE 

      UNION ALL 

        SELECT 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[COMBO])) AS category, 
         LTRIM(RTRIM([CHARGE])) AS charge, 
         ''True'' AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''0CPTMore'' AS sourceTable, 
         ''02'' AS sequenceSet 
        FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [CPTMORE] AS CPT 
         ON CPT.[LEVEL] LIKE PP.[LEVEL] + ''%'' 
        WHERE 
         (CPT.[COMBO] IS NULL OR CPT.[COMBO] NOT IN (''Editor'',''MOD'',''CATEGORY'',''Types'',''Bundles'')) 
         AND CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 

      UNION ALL 

      SELECT 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[COMBO])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         ''True'' AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''0CPTMore'' AS sourceTable, 
         ''03'' AS sequenceSet 
        FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [CPTMORE] AS CPT 
         ON CPT.[LEVEL] LIKE ''%'' + PP.[LEVEL] + ''%'' 
        WHERE 
         (CPT.[COMBO] IS NULL OR CPT.[COMBO] NOT IN (''Editor'',''MOD'',''CATEGORY'',''Types'',''Bundles'')) 
         AND CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 

      ) AS CPTS 
      ORDER BY 
       procedureCode, sourceTable, [description] 
     END -- Excluded master from search 
    ELSE 
     BEGIN -- Including master in search, but present favorites before master for each code 
      -- Get matching procedures, ordered by code, source (favorites first), and description. 
      -- There probably will be procedures with duplicated code+description, so we will filter 
      -- duplicates shortly. 
     INSERT INTO #DistinctMatchingCpts (sourceTable, procedureCode, description , category ,charge, active, rvu, sequenceSet) 
     SELECT DISTINCT sourceTable, procedureCode, description, category ,charge, active, rvu, sequenceSet 
      FROM (
        SELECT TOP 1 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[COMBO])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         ''True'' AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''0CPTMore'' AS sourceTable, 
         ''00'' AS sequenceSet 
       FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [CPTMORE] AS CPT 
         ON CPT.[LEVEL] = PP.[LEVEL] 
        WHERE 
         (CPT.[COMBO] IS NULL OR CPT.[COMBO] NOT IN (''Editor'',''MOD'',''CATEGORY'',''Types'',''Bundles'')) 
         AND CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 
        ORDER BY PP.CODE 

        UNION ALL 

        SELECT TOP 1 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[CATEGORY])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         COALESCE(CASE [ACTIVE] WHEN 1 THEN ''True'' WHEN 0 THEN ''False'' WHEN '''' THEN ''False'' ELSE ''False'' END,''True'') AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''2MasterCPT'' AS sourceTable, 
         ''00'' AS sequenceSet 
        FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [MASTERCPT] AS CPT 
         ON CPT.[LEVEL] = PP.[LEVEL] 
        WHERE 
         CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 
        ORDER BY PP.CODE 

        UNION ALL 

        SELECT 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[COMBO])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         ''True'' AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''0CPTMore'' AS sourceTable, 
         ''01'' AS sequenceSet 
       FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [CPTMORE] AS CPT 
         ON CPT.[LEVEL] = PP.[LEVEL] 
        WHERE 
         (CPT.[COMBO] IS NULL OR CPT.[COMBO] NOT IN (''Editor'',''MOD'',''CATEGORY'',''Types'',''Bundles'')) 
         AND CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 

        UNION ALL 

        SELECT 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[CATEGORY])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         COALESCE(CASE [ACTIVE] WHEN 1 THEN ''True'' WHEN 0 THEN ''False'' WHEN '''' THEN ''False'' ELSE ''False'' END,''True'') AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''2MasterCPT'' AS sourceTable, 
         ''01'' AS sequenceSet 
        FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [MASTERCPT] AS CPT 
         ON CPT.[LEVEL] = PP.[LEVEL] 
        WHERE 
         CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 

        UNION ALL 

        SELECT TOP 1 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[COMBO])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         ''True'' AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''0CPTMore'' AS sourceTable, 
         ''02'' AS sequenceSet 
       FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [CPTMORE] AS CPT 
         ON CPT.[LEVEL] LIKE PP.[LEVEL] + ''%'' 
        WHERE 
         (CPT.[COMBO] IS NULL OR CPT.[COMBO] NOT IN (''Editor'',''MOD'',''CATEGORY'',''Types'',''Bundles'')) 
         AND CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 
        ORDER BY PP.CODE 

        UNION ALL 

        SELECT TOP 1 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[CATEGORY])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         COALESCE(CASE [ACTIVE] WHEN 1 THEN ''True'' WHEN 0 THEN ''False'' WHEN '''' THEN ''False'' ELSE ''False'' END,''True'') AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''2MasterCPT'' AS sourceTable, 
         ''02'' AS sequenceSet 
        FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [MASTERCPT] AS CPT 
         ON CPT.[LEVEL] LIKE PP.[LEVEL] + ''%'' 
        WHERE 
         CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 
        ORDER BY PP.CODE 

        UNION ALL 

        SELECT 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[COMBO])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         ''True'' AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''0CPTMore'' AS sourceTable, 
         ''03'' AS sequenceSet 
       FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [CPTMORE] AS CPT 
         ON CPT.[LEVEL] LIKE PP.[LEVEL] + ''%'' 
        WHERE 
         (CPT.[COMBO] IS NULL OR CPT.[COMBO] NOT IN (''Editor'',''MOD'',''CATEGORY'',''Types'',''Bundles'')) 
         AND CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 

        UNION ALL 

        SELECT 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[CATEGORY])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         COALESCE(CASE [ACTIVE] WHEN 1 THEN ''True'' WHEN 0 THEN ''False'' WHEN '''' THEN ''False'' ELSE ''False'' END,''True'') AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''2MasterCPT'' AS sourceTable, 
         ''03'' AS sequenceSet 
        FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [MASTERCPT] AS CPT 
         ON CPT.[LEVEL] LIKE PP.[LEVEL] + ''%'' 
        WHERE 
         CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 

        UNION ALL 

        SELECT 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[COMBO])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         ''True'' AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''0CPTMore'' AS sourceTable, 
         ''04'' AS sequenceSet 
       FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [CPTMORE] AS CPT 
         ON CPT.[LEVEL] LIKE ''%'' + PP.[LEVEL] + ''%'' 
        WHERE 
         (CPT.[COMBO] IS NULL OR CPT.[COMBO] NOT IN (''Editor'',''MOD'',''CATEGORY'',''Types'',''Bundles'')) 
         AND CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 

        UNION ALL 

        SELECT 
         LTRIM(RTRIM(CPT.[CODE])) AS procedureCode, 
         LTRIM(RTRIM(CPT.[LEVEL])) AS description, 
         LTRIM(RTRIM(CPT.[CATEGORY])) AS category, 
         LTRIM(RTRIM(CPT.[CHARGE])) AS charge, 
         COALESCE(CASE [ACTIVE] WHEN 1 THEN ''True'' WHEN 0 THEN ''False'' WHEN '''' THEN ''False'' ELSE ''False'' END,''True'') AS active, 
         LTRIM(RTRIM([RVU])) AS rvu, 
         ''2MasterCPT'' AS sourceTable, 
         ''04'' AS sequenceSet 
        FROM 
        @ProcedureSearchPhrases PP 
        INNER JOIN [MASTERCPT] AS CPT 
         ON CPT.[LEVEL] LIKE ''%'' + PP.[LEVEL] + ''%'' 
        WHERE 
         CPT.[CODE] IS NOT NULL 
         AND CPT.[CODE] NOT IN (''0'', '''') 
        AND CPT.[CODE] NOT IN (SELECT CPTE.[CODE] FROM CPT AS CPTE WHERE CPTE.[CODE] IS NOT NULL) 

      ) AS CPTS 

      ORDER BY 
       sequenceSet, sourceTable, [description] 

     END 

     /* Final select - uses artificial ordering from the insertion ORDER BY */ 
     SELECT procedureCode, description, category, rvu, charge, active FROM 
     ( 
     SELECT TOP 500 *-- procedureCode, description, category, rvu, charge, active 
     FROM #DistinctMatchingCpts 
     ORDER BY sequenceSet, sourceTable, description 

     ) AS CPTROWS 

     DROP TABLE #DistinctMatchingCpts 

Sin embargo, esto NO cumple los criterios de mejor coincidencia en el recuento de palabras (como en el valor de la fila 1 en la muestra) que debe coincidir con el mejor recuento de palabras (encontrado) de esa fila.

Tengo control total sobre la forma/formato del parámetro de valor de tabla si eso hace la diferencia.

Estoy devolviendo este resultado a un programa C# si eso es útil.

+0

¿Alguna de estas respuestas a su pregunta? –

+0

Varias respuestas, algunas ideas pero ninguna completamente suficiente para obtener un conjunto de resultados completo que cumpla la lista de criterios. En la actualidad, estoy creando un prototipo de un algoritmo que parece estar haciendo lo que quiero; una vez que lo haya examinado por completo, determinaré si es una solución viable que cumpla esos objetivos. –

Respuesta

0

Parece que está buscando un algoritmo coincidente, que puede ser difícil de crear sin el uso de procedimientos almacenados. De la experiencia anterior, hay edit distance algorithms (como Levenshtein) que son muy útiles para determinar la similitud. Estos devuelven un número, a veces una serie de diferencias entre cadenas, en el que puede crear su propia ecuación de ponderación para dar una puntuación. A continuación, puede crear clasificaciones o umbrales para que los puntajes disminuyan los falsos negativos/positivos.

+0

Algo así, pero los términos son MUY específicos y mantienen un conjunto limitado de palabras, todas específicas para que una "coincidencia exacta" sea suficiente para mis propósitos, solo necesito obtener varios "juegos" de coincidencias exactas basadas en la prioridad como se describe. Buena sugerencia, sin embargo. –

+0

También puedo usar procedimientos almacenados o lo que sea necesario para lograr un conjunto de resultados adecuado. –

+0

Buena sugerencia con la distancia de Levenshtein, sin embargo, es para letras, para ser utilizada en la comparación de términos. Ahora aquí está la parte divertida: esta es la respuesta correcta: distancia de Levenshtein. Lo que estás tratando de lograr es la distancia de Levenshtein, pero no en letras, sino en palabras. Sugiero que, si es posible, crear un conjunto CLR para calcular esta distancia. Una distancia de palabra-Levenshtein de 1 significaría que una palabra (no una letra) no está en su lugar (se supone que no debe estar allí o falta). Entonces puedes ordenar fácilmente por esta distancia. – AlexanderMP

4

Necesita poder dividir cadenas para resolver este problema. I prefer the number table approach to split a string in TSQL

Para mi código siguiente para trabajar (así como mi función split), que tiene que hacer esta configuración de la tabla una vez:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number 
    INTO Numbers 
    FROM sys.objects s1 
    CROSS JOIN sys.objects s2 
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number) 

Una vez que los números de la tabla instalación, la creación de esta función de división:

CREATE FUNCTION [dbo].[FN_ListToTable] 
(
    @SplitOn char(1)  --REQUIRED, the character to split the @List string on 
    ,@List  varchar(8000)--REQUIRED, the list to split apart 
) 
RETURNS TABLE 
AS 
RETURN 
(

    ---------------- 
    --SINGLE QUERY-- --this will not return empty rows 
    ---------------- 
    SELECT 
     ListValue 
     FROM (SELECT 
        LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue 
        FROM (
          SELECT @SplitOn + @List + @SplitOn AS List2 
         ) AS dt 
         INNER JOIN Numbers n ON n.Number < LEN(dt.List2) 
        WHERE SUBSTRING(List2, number, 1) = @SplitOn 
      ) dt2 
     WHERE ListValue IS NOT NULL AND ListValue!='' 

); 
GO 

no dude en hacer su propia función de división, pero todavía se necesita la tabla números de mi solución para trabajar.

Ahora se puede dividir fácilmente una cadena CSV en una mesa y unirse en él:

select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,') 

SALIDA:

ListValue 
----------------------- 
1 
2 
3 
4 
5 
6777 

(6 row(s) affected) 

ahora a probar esto:

DECLARE @BaseTable table (RowID int primary key, RowValue varchar(100)) 
set nocount on 
INSERT @BaseTable VALUES (1,'The cows came home empty handed') 
INSERT @BaseTable VALUES (2,'This is my string as a test template to rank')       -- third 
INSERT @BaseTable VALUES (3,'pencil pen paperclip eraser') 
INSERT @BaseTable VALUES (4,'wow') 
INSERT @BaseTable VALUES (5,'no dice here') 
INSERT @BaseTable VALUES (6,'This is my string as a test template to rank on even though not exact.') -- second 
INSERT @BaseTable VALUES (7,'apple banana pear grape lemon orange kiwi strawberry peach watermellon') 
INSERT @BaseTable VALUES (8,'This is my string as a test template')         -- 5th 
INSERT @BaseTable VALUES (9,'rat cat bat mat sat fat hat pat ') 
INSERT @BaseTable VALUES (10,'house home pool roll') 
INSERT @BaseTable VALUES (11,'This is my string as a test template to')        -- 4th 
INSERT @BaseTable VALUES (12,'talk wisper yell scream sing hum') 
INSERT @BaseTable VALUES (13,'This is my string as a test template to rank on.')      -- first 
INSERT @BaseTable VALUES (14,'aaa bbb ccc ddd eee fff ggg hhh') 
INSERT @BaseTable VALUES (15,'three twice three once twice three') 
set nocount off 

DECLARE @SearchValue varchar(100) 
SET @SearchValue='This is my value string as a test template to rank on.' 

;WITH SplitBaseTable AS --expand each @BaseTable row into one row per word 
(SELECT 
    b.RowID, b.RowValue, s.ListValue 
    FROM @BaseTable b 
     CROSS APPLY dbo.FN_ListToTable(' ',b.RowValue) AS s 
) 
, WordMatchCount AS --for each @BaseTable row that has has a word in common withe the search string, get the count of matching words 
(SELECT 
    s.RowID,COUNT(*) AS CountOfWordMatch 
    FROM dbo.FN_ListToTable(' ',@SearchValue) v 
     INNER JOIN SplitBaseTable    s ON v.ListValue=s.ListValue 
    GROUP BY s.RowID 
    HAVING COUNT(*)>0 
) 
, SearchLen AS --get one row for each possible length of the search string 
(
SELECT 
    n.Number,SUBSTRING(@SearchValue,1,n.Number) AS PartialSearchValue 
    FROM Numbers n 
    WHERE n.Number<=LEN(@SearchValue) 
) 
, MatchLen AS --for each @BaseTable row, get the max starting length that matches the search string 
(
SELECT 
    b.RowID,MAX(l.Number) MatchStartLen 
    FROM @BaseTable     b 
     LEFT OUTER JOIN SearchLen l ON LEFT(b.RowValue,l.Number)=l.PartialSearchValue 
    GROUP BY b.RowID 
) 
SELECT --return the final search results 
    b.RowValue,w.CountOfWordMatch,m.MatchStartLen 
    FROM @BaseTable      b 
     LEFT OUTER JOIN WordMatchCount w ON b.RowID=w.RowID 
     LEFT OUTER JOIN MatchLen  m ON b.RowID=m.RowID 
    WHERE w.CountOfWordMatch>0 
    ORDER BY w.CountOfWordMatch DESC,m.MatchStartLen DESC,LEN(b.RowValue) DESC,b.RowValue ASC 

SALIDA:

RowValue                CountOfWordMatch MatchStartLen 
----------------------------------------------------------------------- ---------------- ------------- 
This is my string as a test template to rank on.      11    11 
This is my string as a test template to rank on even though not exact. 10    11 
This is my string as a test template to rank       10    11 
This is my string as a test template to         9    11 
This is my string as a test template         8    11 

(5 row(s) affected) 

Hace que el comienzo de la cadena de palabras coincida con un poco diferente, en el sentido de que observa el número de caracteres desde el comienzo de la cadena que coinciden.

Una vez que funcione, puede intentar optimizarlo creando algunas tablas indizadas estáticas para SplitBaseTable. Posiblemente use un disparador en su @BaseTable.

+0

Esta es una idea interesante. Los desafíos actuales son: ni un separador de palabras como:) (,/<;& space<> =% -] [todos existen y son algo significativos como en las frases entre comillas dobles: "Nivel> 9.0%" o "LDL-C <100 mg/dl "," LDL-C 100-129 mg/dl "," LDL-C = 130 mg/dl "SO Tendré que encontrar la manera de resolver ese problema, tal vez mediante múltiples separadores para crear el índice estático como sugiera. para una lista de "palabras/frases", complicada aún más por una clave única en la tabla base en uso. –

+0

Tiene tres opciones con respecto a las reglas de división de palabra/frase "loca": 1) escribir una rutina dividida CLR para manejar toda la lógica necesaria. 2) inserte un solo carácter como 'PRINT CHAR (182)' dentro de sus cadenas para identificar claramente las divisiones. 3) rediseñe la tabla para que cada "frase" ya esté dividida en su propia fila, y se pueden reconstruir en función de una ID y un número de secuencia. En cuanto a la clave principal, agregue una columna de identidad como se muestra aquí: http://blog.sqlauthority.com/2009/05/03/sql-server-add-or-remove-identity-property-on-column/ y make es el PK –

+0

+1 para el mejor potencial de solución en este momento. –

0

Hace un tiempo tuve una pregunta similar. La pregunta que estaba tratando de responder fue cuántas palabras coinciden entre dos columnas diferentes, y el rango se basa en el porcentaje más alto de palabras coincidentes. Fue mucho más allá de mí, pero recibí una respuesta fantástica de Martin.

Consulte su respuesta en my question here.

0

Una respuesta a todos sus problemas: utilice sphynx http://sphinxsearch.com y no resuelva esto en SQL.

Sphynx es de código abierto, funciona con todas las bases de datos y todos los sistemas operativos.

Eso es lo que craigslist está usando.

Es el mejor sistema externo de búsqueda de texto completo en el momento de esta publicación. Ordenará sus resultados en la relevancia que está solicitando y no necesitará tablas SQL sofisticadas o procedimientos SQL. Intentalo.

+0

A veces, para bien o para mal, debe recuperar los registros utilizando SQL (por ejemplo, si también los está filtrando según otros criterios, y si los está vinculando a tablas relacionadas). –

Cuestiones relacionadas