2010-06-26 8 views
10

Desidero confrontare le singole parole dell'input dell'utente con le singole parole di una colonna della tabella.SQL: divisione di una colonna in più parole per cercare l'input dell'utente

Per esempio, consideriamo queste righe nel mio tavolo:

ID Name 
1 Jack Nicholson 
2 Henry Jack Blueberry 
3 Pontiac Riddleson Jack 

Si consideri che l'input dell'utente è 'Pontiac Jack'. Voglio assegnare pesi/gradi per ogni partita, quindi non posso usare una coperta LIKE (WHERE Name LIKE @SearchString).

Se Pontiac è presente in qualsiasi riga, voglio assegnarlo 10 punti. Ogni match per Jack ottiene altri 10 punti, ecc. Quindi la riga 3 otterrebbe 20 punti e le righe 1 e 2 ottengono 10.

Ho diviso l'input dell'utente in singole parole e le ho archiviate in una tabella temporanea @SearchWords (Parola).

Ma non riesco a capire un modo per avere un'istruzione SELECT che mi permetta di combinare questo. Forse sto andando su questo nel modo sbagliato?

Cheers, WT

+1

hai in mente di utilizzare SQL SErver Ricerca fulltext? –

+0

Sì, ho - non funzionava bene per noi, ed era molto difficile personalizzarlo in base alle nostre esigenze. –

+1

+1 per la ricerca fullText - non necessariamente SQL Server, ma per esempio lucene.net. –

risposta

1

Per SQL Server, provate questo:

SELECT Word, COUNT(Word) * 10 AS WordCount 
FROM SourceTable 
INNER JOIN SearchWords ON CHARINDEX(SearchWords.Word, SourceTable.Name) > 0 
GROUP BY Word 
+0

Soluzione piacevole ed elegante. Immagino che il tavolo dell'OP dovrebbe avere qualcosa che colleghi le singole parole alla frase di ricerca originale - quindi ottenere il punteggio per l'intera frase sarebbe semplice come unire la frase a questo e sommare il conteggio delle parole raggruppato per l'intera frase. Bel nome utente btw ...uno dei miei preferiti xkcds :) –

0

Che dire di questo? (Questa è la sintassi di MySQL, penso che si deve solo sostituire il CONCAT e farlo con +)

SELECT names.id, count(searchwords.word) FROM names, searchwords WHERE names.name LIKE CONCAT('%', searchwords.word, '%') GROUP BY names.id 

Poi si avrebbe un risultato SQL con l'ID del nome-tavolo e il conteggio delle parole che corrispondono a a quell'id.

0

si potrebbe fare con un'espressione di tabella comune che funziona la ponderazione. Per esempio:

--** Set up the example tables and data 
DECLARE @Name TABLE (id INT IDENTITY, name VARCHAR(50)); 
DECLARE @SearchWords TABLE (word VARCHAR(50)); 

INSERT INTO @Name 
     (name) 
VALUES ('Jack Nicholson') 
     ,('Henry Jack Blueberry') 
     ,('Pontiac Riddleson Jack') 
     ,('Fred Bloggs'); 

INSERT INTO @SearchWords 
     (word) 
VALUES ('Jack') 
     ,('Pontiac'); 

--** Example SELECT with @Name selected and ordered by words in @SearchWords 
WITH Order_CTE (weighting, id) 
AS (
    SELECT COUNT(*) AS weighting 
     , id 
     FROM @Name AS n 
     JOIN @SearchWords AS sw 
     ON n.name LIKE '%' + sw.word + '%' 
    GROUP BY id 
) 
SELECT n.name 
    , cte.weighting 
    FROM @Name AS n 
    JOIN Order_CTE AS cte 
    ON n.id = cte.id 
ORDER BY cte.weighting DESC; 

Utilizzando questa tecnica, è possibile applicare anche un valore ad ogni parola di ricerca se si voleva. Quindi potresti rendere Jack più prezioso di Pontiac. Questo sarebbe simile a questa:

--** Set up the example tables and data 
DECLARE @Name TABLE (id INT IDENTITY, name VARCHAR(50)); 
DECLARE @SearchWords TABLE (word VARCHAR(50), value INT); 

INSERT INTO @Name 
     (name) 
VALUES ('Jack Nicholson') 
     ,('Henry Jack Blueberry') 
     ,('Pontiac Riddleson Jack') 
     ,('Fred Bloggs'); 

--** Set up search words with associated value 
INSERT INTO @SearchWords 
     (word, value) 
VALUES ('Jack',10) 
     ,('Pontiac',20) 
     ,('Bloggs',40); 


--** Example SELECT with @Name selected and ordered by words and values in @SearchWords 
WITH Order_CTE (weighting, id) 
AS (
    SELECT SUM(sw.value) AS weighting 
     , id 
     FROM @Name AS n 
     JOIN @SearchWords AS sw 
     ON n.name LIKE '%' + sw.word + '%' 
    GROUP BY id 
) 
SELECT n.name 
    , cte.weighting 
    FROM @Name AS n 
    JOIN Order_CTE AS cte 
    ON n.id = cte.id 
ORDER BY cte.weighting DESC;  
0

mi sembra che la cosa migliore da fare sarebbe quella di mantenere un tavolo separato con tutte le singole parole. Ad esempio:

ID  Word  FK_ID 
1  Jack  1 
2  Nicholson 1 
3  Henry  2 
(etc) 

Questa tabella sarebbe tenuto aggiornato con i trigger, e si avrebbe un indice non cluster sulla 'Parola', 'FK_ID'. Quindi l'SQL per produrre le tue ponderazioni sarebbe semplice ed efficiente.

0

Che ne dite di qualcosa di simile ....

Select id, MAX(names.name), count(id)*10 from names 
inner join @SearchWords as sw on 
    names.name like '%'+sw.word+'%' 
group by id 

assumendo che tabella con i nomi chiamati "nomi".

Problemi correlati