Recherche de text libre SQL Server: search de mots à partir d'une phrase dans deux arrays

J'ai une table avec des entresockets et une avec des catégories. J'utilise la search de text libre de SQL Server, et la search d'entresockets (par nom et description) fonctionne bien. Mais maintenant je veux aussi inclure la table des catégories.

Je veux searchr quelque chose comme: ABC 24 Supermarket .

Maintenant, ABC 24 devrait faire une correspondance avec la colonne Name de la table de la company , et Supermarket est le nom de la category cette société est connectée.

En ce moment j'ai quelque chose comme ça:

 DECLARE @SearchSsortingng VARCHAR(100) = '"ABC 24 Supermarket"' SELECT * FROM Company CO INNER JOIN Category CA ON CA.CategoryId = CO.CategoryId WHERE CONTAINS((CO.[Description], CO.[Name]), @SearchSsortingng) AND CONTAINS(CA.[Description], @SearchSsortingng) 

Mais bien sûr, cela ne me donne rien, parce que ma string de search ne peut être trouvée ni dans la société ni dans la table des catégories. Quelqu'un at-il une idée sur la façon de faire une search combinée sur mon entreprise et la table de catégorie?

L'idée de séparer les strings, comme suggéré dans la réponse de Lobo ci-dessous, n'est pas vraiment une option. Parce que je ne sais pas quelle partie sera celle qui devrait correspondre à une catégorie et quelle partie devrait être utilisée pour faire correspondre les noms / descriptions d'entreprise. Les users peuvent aussi bien taper "Supermarket ABC 24".

Imho la bonne façon de le faire est de créer une vue indexée contenant la key primaire de la table principale ('company' dans votre exemple) et une deuxième colonne contenant toutes les choses que vous voulez réellement searchr, à savoir

 create View View_FreeTextHelper with schemabinding as select CO.PrimaryKey, -- or whatever your PK is named CO.description +' '+CA.description +' '+CO.whatever as Searchtext from dbo.company CO join dbo.category CA on CA.CategoryId = CO.CategoryId 

Notez la forme en deux parties de vos tables. Quelques ressortingctions en découlent, par exemple toutes les tables impliquées doivent être dans le même espace de table et autant que je me souvienne, aucune colonne TEXT n'est permise dans ce type de concaténation (vous pouvez cependant les lancer).

Créez maintenant un index unique sur la colonne PrimaryKey

 create unique clustered index [View_Index] on View_FreeTextHelper (PrimaryKey ASC) 

Enfin, créez l'index de text intégral sur la vue en utilisant la colonne "Searchtext" comme seule colonne à indexer. Bien sûr, vous pouvez append plus de colonnes, si vous souhaitez par exemple distinguer dans la search du nom et de l'location de l'entreprise et, le nom des gestionnaires (vous les concaténiez dans une deuxième colonne).

Récupérer vos données est maintenant facile:

 select tbl.RANK, co.* from freetextTable(View_FreeTextHelper,Search,'Your searchtext here') tbl join company co on tbl.key=co.PrimaryKey order by tbl.RANK desc 

Vous pouvez également limiter la sortie en utilisant select top 50 car la clause freetexttable returnnera finalement beaucoup de résultats proches et pas si proches.

Et enfin, ne soyez pas confus si vous ne trouvez pas quelque chose comme 'off the shelf inc.' Méfiez-vous des lists d'arrêt. Ce sont des lists de mots qui sont très communs, n'ont pas d'usage sémantique (comme the ) et sont donc supprimés du text à indexer. Pour les inclure, vous devez passer de la fonction Stoplist.

Un dernier conseil: le text intégral est très puissant mais comporte beaucoup de fonctionnalités, astuces et mises en garde. Cela prend un peu de time pour bien comprendre les techniques et get les meilleurs résultats que vous voulez.

S'amuser.

Si nous supposons que le nom des colonnes est unique par ligne, vous pouvez utiliser la requête ci-dessous. L'exemple suivant renvoie toutes les lignes contenant l'expression "ABC", "24" ou "Supermarché" dans chacune des colonnes

 DECLARE @SearchSsortingng nvarchar(100) = N'ABC 24 Supermarket' SET @SearchSsortingng = REPLACE(LTRIM(RTRIM(@SearchSsortingng)), ' ', '|') SELECT * FROM Company CO JOIN Category CA ON CA.CategoryId = CO.CategoryId WHERE CONTAINS(CO.[Name], @SearchSsortingng) AND CONTAINS(CO.[Description], @SearchSsortingng) AND CONTAINS(CA.[Description], @SearchSsortingng) 

Tout d'abord, vous devez préparer une valeur de search pour le prédicat CONTAINS utilisé dans la clause WHERE. Dans ce cas, j'ai remplacé les espaces entre les mots sur le "|" opérateur logique (le symbole de la barre (|) peut être utilisé à la place du mot key OR pour représenter l'opérateur OR).

Il me semble que même si la réponse que j'ai écrite précédemment devrait fonctionner correctement et être raisonnablement efficace, traiter les éléments de search un à la fois et ne chercher dans les résultats de search existants qu'après le premier, qu'il serait plus rapide de faites tout cela en même time, en utilisant SQL dynamic.

Donc, voici une autre solution potentielle à votre problème, que j'entre dans une réponse séparée car elle n'est pas liée à la solution que j'ai déjà posté:

 DECLARE @SearchSsortingng VARCHAR(100) = '"ABC 24 Supermarket"' DECLARE @AllItems table ( SearchItem varchar(100) ), @x int, @cmd varchar(1000), @wc varchar(8000), @this varchar(100); -- break up @SearchSsortingng into component search items: select @x = charindex(' ', @SearchSsortingng); while @x > 0 begin insert @AllItems (SearchItem) values (subssortingng(@SearchSsortingng, 1, @x - 1)); select @SearchSsortingng = subssortingng(@searchssortingng, @x + 1); select @x = charindex(' ', @Searchssortingng); end; -- add the last item insert @AllItems (SearchItem) values (@SearchSsortingng) select @cmd = 'select CO.* from Company CO inner join Category CA on CO.CategoryId = CA.CategoryId WHERE'; --now process search items one-at-a-time building up a where clause to plug into @cmd: while (select count(*) from @AllItems) > 0 begin select @this = min(SearchItem) from @AllItems; delete @AllItems where SearchItem = @this; select @wc = @wc + 'AND (contains ((CO.[Description], [CO.[Name]) ''' + @this + ''') or contains (CA.[Description], ''' + @this + ''') ' end; --ready to go: exec (@cmd + subssortingng(@wc, 4)); --subssortingng removes first AND 

Je ne sais pas si cela va constituer une GRANDE réponse (j'ai tendance à en douter) mais je voulais un problème sur lequel travailler et j'ai eu le plaisir de choisir le vôtre, alors voici ma solution:

 DECLARE @SearchSsortingng VARCHAR(100) = '"ABC 24 Supermarket"' DECLARE @AllItems table ( SearchItem varchar(100) ), @x int; -- break up @SearchSsortingng into component search items: select @x = charindex(' ', @SearchSsortingng); while @x > 0 begin insert @AllItems (SearchItem) values (subssortingng(@SearchSsortingng, 1, @x - 1)); select @SearchSsortingng = subssortingng(@searchssortingng, @x + 1); select @x = charindex(' ', @Searchssortingng); end; -- add the last item insert @AllItems (SearchItem) values (@SearchSsortingng) DECLARE @this varchar(100), -- = current search item @found table ( -- table to contain rows matching the current search item ID int ), @usable table ( -- table to contain rows matching all search items ID int -- already tested ); --now process search items one-at-a-time while (select count(*) from @AllItems) > 0 begin select @this = min(SearchItem) from @AllItems; delete @AllItems where SearchItem = @this; if (select count(*) from @usable) = 0 begin --first search item --for the first item, just find the companies matching this item, in either the --company name or description or category description columns: insert @found (ID) select CO.CompanyID from Company CO inner join Category CA on CO.CategoryID = CA.CategoryID where contains ((CO.[Description], [CO.[Name]) @this) or contains (CA.[Description], @this) end else begin --other search items -- for subsequent items, its got to match with the company name or description -- or category description as above - BUT it's also got to be a company we -- already identified when processing the previous term insert @found (ID) select CO.CompanyID from Company CO inner join Category CA on CO.CategoryID = CA.CategoryID inner join @usable U on CO.CompanyID = U.ID where contains ((CO.[Description], [CO.[Name]) @this) or contains (CA.[Description], @this) end --now clear out and re-populate the usable companies table ready for processing the --next search item delete @usable; insert @usable (ID) select ID from @found; --and clear out the current matches table, ready for the next search item delete @found; end; --whatever is in @usable now, is a match with all the component search items, so: select CO.* from Company CO inner join Category CA on CO.CategoryId = CA.CategoryId inner join @usable U on CO.CompanyID = U.ID; 

L'idée ici est que nous allons parsingr la string en différentes variables, puis searchr les autres variables pour une correspondance.

 DECLARE @SearchSsortingng VARCHAR(100) = '"ABC 24 Supermarket"', @A varchar(100), @B varchar(100), @C varchar(100),@index int set @A = Subssortingng(@searchSsortingng, 1, PATINDEX('% %', @searchSsortingng) -1) set @index = PATINDEX('% %', @searchSsortingng) + 1 set @B = Subssortingng(@searchSsortingng, @index, PATINDEX('% %', @subssortingng(@searchssortingng, @index, 100)) -1) Set @index = PATINDEX('% %', @subssortingng(@searchssortingng, @index, 100)) + 1 set @C = Subssortingng(@searchSsortingng, @index, PATINDEX('% %', @subssortingng(@searchssortingng, @index, 100)) -1) SELECT * FROM Company CO INNER JOIN Category CA ON CA.CategoryId = CO.CategoryId WHERE CO.[Description] like @A or CO.[Description] like @B or CO.[Description] like @c or CO.[Name] like @A or CO.[Name] like @B or CO.[Name] like @C or CA.[Description] like @A or CA.[Description] like @B or CA.[Description] like @C 

Ce code me semble moche mais il devrait remplir les conditions pour que l'user saisisse jusqu'à 3 éléments à searchr. Quelqu'un a des suggestions pour le nettoyer?

cela devrait fonctionner.

 DECLARE @SearchSsortingng VARCHAR(100) = '"ABC 24 Supermarket"' set @SearchSsortingng = replace(@SearchSsortingng,' ','" or "') SELECT * FROM Company CO INNER JOIN Category CA ON CA.CategoryId = CO.CategoryId WHERE CONTAINS((CO.[Description], CO.[Name]), @SearchSsortingng) AND CONTAINS(CA.[Description], @SearchSsortingng) 

l'espoir aide un peu

Pourquoi ne renverses-tu pas ta logique? Votre exemple essayait de find la string de search dans vos valeurs de champs, mais ce que vous voulez vraiment faire est de find vos valeurs de champs dans votre string de search, non?

  DECLARE @SearchSsortingng VARCHAR(100) = '"ABC 24 Supermarket"' SELECT * FROM Company CO INNER JOIN Category CA ON CA.CategoryId = CO.CategoryId WHERE (CONTAINS(@SearchSsortingng, CO.[Description]) OR CONTAINS(@SearchSsortingng, CO.[Name])) AND CONTAINS(@SearchSsortingng, CA.[Description]) 

( Je n'ai pas d'installation de sql-server pour essayer CONTAINS Vous pouvez replace la column LIKE '%ssortingng%' par CONTAINS(column, 'ssortingng') et essayer. )

Voir toutes les questions ici.


Une autre mise à jour – Après avoir lu les autres réponses et le manuel, il semble que vous n'avez pas besoin de valeurs entre parenthèses dans la string contient contrairement à ce que j'attendais. Cela devrait donc fonctionner aussi – (vous pouvez même essayer ' | ' au lieu de ' OR '

 SELECT CO.name, CA.description FROM company CO INNER JOIN category CA ON CA.CategoryId = CO.CategoryId WHERE CONTAINS((CO.name,CO.description), REPLACE('ABC 25 SuperMarket', ' ', ' OR ')) AND CONTAINS(CA.description, REPLACE('ABC 25 SuperMarket', ' ', ' OR ')) 

S'il se plaint d'une erreur de syntaxe proche de replace, vous pouvez créer une string de search DECLARE @SearchSsortingng varchar(MAX) = REPLACE('ABC 25 SuperMarket',' ', ' OR ') et l'utiliser à la place de replace(......) comme deuxième argument.


Mise à jour selon votre question modifiée –

Premièrement, vous devriez déplacer la logique au niveau de l'application si possible. Je pense que c'est trop difficile à gérer ici. Je suis venu avec cette requête, mais notez que cela va split chaque mot et la searchr dans le name et la description sorte que vous finirez par get quelques résultats de plus que ce que vous pourriez penser. Par exemple, cela returnnera tous les Supermarket qui ont soit ABC ou 24 dans leur nom par rapport à returnner un seul Supermarket avec le nom ABC 24 dans ma requête précédente. Cela devrait vraiment vous aider parce que, selon vous, l'user pourrait simplement taper "ABC Supermarket 24" or "24 ABC Supermarket" or ...

 DECLARE @SearchSsortingng varchar(MAX) = 'ABC 24 SuperMarket' DECLARE @separator varchar(MAX) = ' ' DECLARE @Like1 varchar(MAX) = 'CO.name LIKE' DECLARE @Like2 varchar(MAX) = 'CA.description LIKE' DECLARE @WHERE1 varchar(MAX) = '( ' + @Like1 + ' ''%' + REPLACE(@SearchSsortingng,@separator,'%'' OR ' + @Like1 + ' ''%')+'%'')' DECLARE @WHERE2 varchar(MAX) = '( ' + @Like2 + ' ''%' + REPLACE(@SearchSsortingng,@separator,'%'' OR ' + @Like2 + ' ''%')+'%'')' DECLARE @QuerySsortingng varchar(MAX) = CONCAT('SELECT CO.name, CA.description FROM company CO INNER JOIN category CA ON CA.CategoryId = CO.CategoryId WHERE ', @WHERE1, ' AND ', @WHERE2) exec(@QuerySsortingng); 

Si vous produisez @WHERE1 vous devriez voir

 ( CO.name LIKE '%ABC%' OR CO.name LIKE '%25%' OR CO.name LIKE '%SuperMarket%') 

Comme je l'ai déjà dit, vous pouvez essayer d'utiliser CONTAINS avec des valeurs entre parenthèses comme

 DECLARE @SearchSsortingng varchar(MAX) = 'ABC 25 SuperMarket' DECLARE @separator varchar(MAX) = ' ' DECLARE @WHERESsortingng varchar(MAX) = '''"' + REPLACE(@SearchSsortingng, @separator, '" OR "')+'"''' SELECT CO.name, CA.description FROM company CO INNER JOIN category CA ON CA.CategoryId = CO.CategoryId WHERE CONTAINS((CO.name,CO.description), @WHERESsortingng) AND CONTAINS(CA.description, @WHERESsortingng) 

Si vous produisez @WHERESsortingng vous devriez voir

 '"ABC" OR "25" OR "SuperMarket"' 

Réponse précédente:

Cela supposera que le mot après le dernier espace est la description et le repos est le `nom.

Vous pouvez split la string de search et les utiliser comme indiqué ci-dessous. Cette requête utilise like car je n'ai pas d'installation sql-server.

 DECLARE @SearchSsortingng VARCHAR(100) = 'ABC 24 Supermarket' DECLARE @searchLength int = len(@SearchSsortingng) DECLARE @searchReverse VARCHAR(100) = reverse(@SearchSsortingng) SELECT CO.name, CA.description FROM company CO INNER JOIN category CA ON CA.CategoryId = CO.CategoryId WHERE CO.name LIKE concat( '%', SUBSTRING(@SearchSsortingng,0,@searchLength-charindex(' ',@searchReverse)+1), '%') AND CA.description LIKE concat( '%', SUBSTRING(@SearchSsortingng,@searchLength-charindex(' ',@searchReverse)+2,@searchLength), '%') 

Cela devrait fonctionner. Veuillez noter que la clause where utilise AND au lieu de OR.

 DECLARE @SearchSsortingng VARCHAR(100) = 'ABC 24 Supermarket' DECLARE @searchLength int = len(@SearchSsortingng) DECLARE @searchReverse VARCHAR(100) = reverse(@SearchSsortingng) DECLARE @company VARCHAR(100) = SUBSTRING(@SearchSsortingng,0,@searchLength-charindex(' ',@searchReverse)+1) DECLARE @category VARCHAR(100) = SUBSTRING(@SearchSsortingng,@searchLength-charindex(' ',@searchReverse)+2,@searchLength) SELECT CO.name, CA.description FROM company CO INNER JOIN category CA ON CA.CategoryId = CO.CategoryId WHERE CONTAINS((CO.name, CO.description), @company) AND CONTAINS(CA.description , @category) 

vous pouvez utiliser FREETEXT au lieu de CONTAIN.

 DECLARE @SearchSsortingng VARCHAR(100) = '"ABC 24 Supermarket"' SELECT * FROM Company CO INNER JOIN Category CA ON CA.CategoryId = CO.CategoryId WHERE FREETEXT((CO.[Description], CO.[Name]), @SearchSsortingng) OR FREETEXT(CA.[Description], @SearchSsortingng)