2009-07-08 7 views
0

Je suis encore une fois en difficulté avec SQL avec ma fonction de recherche.Algorithme SQL Problème - pour ajouter et filtrer les résultats de la boucle

Fondamentalement, je veux un proc stocké qui sépare une chaîne en mots puis pour chaque mot que je veux vérifier en utilisant la recherche de texte intégral si elle est dans l'un des 2 tableaux.

Si les résultats sont trouvés dans ces tableaux, il convient de les ajouter aux résultats trouvés pour les autres mots et de retourner un jeu où l'enregistrement se trouve dans ces deux ensembles.

Voici donc mon algorithme

if null return all restaurants 

declare results = empty 

for each word 
if in restaurant.name or cuisine.name 
    addRowsToResults 
else if in restaurant.city 
    addRowsToResults 
else if in restaurant.postcode 
    addRowsToResults 


addRowsToResults 
results = (results + new results) where a match is in both 

Je ne sais pas où commencer et je l'ai recherché Google pour les âges, mais d'être un débutant dans SQL je manquerai certains termes.

Aussi est-ce la meilleure façon de faire ce genre de chose?

Appréciez toute aide.

Editer: Juste pour donner plus d'informations. La ville, le code postal et le nom sont tous des champs nvarchar dans une table Restaurants. Cuisine nom est dans une table différente et est liée par une autre table aux restaurants comme un restaurant peut servir de nombreux types de cuisine.

Tous ces champs ont l'indexation complète de recherche de texte.

+0

Vous voulez "AND" ou "OR"? Vous indiquez «ET», mais cela voudrait dire que le mot recherché devrait être dans les champs restaurant, cuisine, ville et code postal. Donc, pour tous ces restaurants nommés "21201" dans "21201", MD (code postal "21201") qui servent "21201" pour le dîner, ce serait juste sur ... – GalacticCowboy

+0

vous n'avez pas vraiment besoin de fulltext pour les codes postaux et Tout simplement vieux index va fonctionner. De plus, vous utilisez du fulltext pour quelque chose pour lequel il n'a pas été conçu, il a été conçu pour indexer de gros morceaux de texte, pas pour 2,3 mots. –

+0

Je veux d'abord vérifier si le mot est dans le nom du restaurant ou de la cuisine (comme un restaurant peut être appelé meilleur chinois par exemple) et si aucun match je veux vérifier si le mot est en ville, si toujours pas de match je veux vérifier si c'est dans le code postal. Ainsi, les résultats sont filtrés lorsque l'utilisateur tape des mots dans une zone de texte. – ddd

Répondre

1

Ce n'est pas vraiment la façon dont SQL fonctionne. Vous pouvez obtenir une partie de cette même fonctionnalité, mais cela ne ressemblera en rien à ce que vous demandez; vous écrivez (naturellement) dans un style plus procédural. D'une part, "ajouter des résultats" essaie de rassembler (vraisemblablement) des éléments disparates dans une même collection. Vous êtes - je pense - en train de demander des restaurants et des cuisines et des villes et des codes postaux pour aller dans la même collection, et le seul type de collection SQL est les tables, et dans une table, toutes les lignes ont le même type.

Mais peut-être restaurants et la cuisine et la ville et le code postal sont tous les champs de la même table? Eh bien, alors, pour un mot donné, vous pourriez dire

SELECT * 
FROM your_table 
WHERE restaurant like "%" + word + "%" 
OR  cuisine like "%" + word + "%" 
OR  city  like "%" + word + "%" 
OR  postcode like "%" + word + "%"; 

Cela commence à se compliquer lorsque vous voulez faire correspondre plusieurs mots; cette réponse est conçue comme un point de départ; peut-être une fois que vous êtes plus familier avec SQL, vous pouvez poser des questions plus faciles à répondre. Bonne chance!

Mise à jour en fonction de vos descriptions de table

SELECT r.* 
FROM  restaurant r 
INNER JOIN link  k ON k.restaurant_id = r.restaurant_id 
INNER JOIN cuisine c on c.cuisine_id = k.cuisine_id 
WHERE r.restaurant like "%" + word + "%" 
OR  c.name  like "%" + word + "%" 
OR  r.city  like "%" + word + "%" 
OR  r.postcode like "%" + word + "%"; 

Et ne vous inquiétez pas qui enregistre reviennent "première". Votre base de données sera assez rapide pour que cela ne vous inquiète pas.

+0

C'est quelque chose de similaire à ce que j'avais avant mais la chose que j'essaie de faire est de faire correspondre plusieurs mots entrés dans n'importe quel ordre. D'où l'ordre de mes instructions if dans mon pseudo code.La cuisine est le seul domaine dans un tabe différent car il est lié par une autre table comme un restaurant peut servir de nombreux types de cuisine – ddd

0

Même si je le voulais, je ne pourrais pas vous donner l'histoire complète à ce sujet - vous n'avez pas posté les définitions de table ou des exemples de données.

Cela dit, je vais faire quelques suppositions. Je pense tout d'abord que vous avez besoin d'utiliser SQL d'une manière basée sur un ensemble. Ne pensez pas en termes de traitement ligne par ligne - traitez un ensemble. Puis combinez les ensembles ensemble.

+0

J'ai mis à jour ma question pour inclure plus d'informations sur les tables. – ddd

1

Il y a une division en mots function here.Ce qui renvoie une table à laquelle vous pouvez vous joindre. Vous devriez également étudier métaphone ou soundex, mais pour soutenir que vous aurez besoin de pré-indexer vos données dans un tableau séparé ou précalculer les codes de méthaphone ou de soundex pour chaque mot dans un champ séparé et indexer ainsi .

1

Cela ressemble à votre tentative d'approche procédurale d'un langage de requête déclaratif basé sur un ensemble. Tout d'abord, vous pouvez diviser une chaîne en un ensemble de résultats à l'aide d'une fonction définie par l'utilisateur. Quelque chose comme ce qui suit fera -

CREATE function [dbo].[csl_to_table] (@list nvarchar(MAX)) 
RETURNS @list_table TABLE ([id] nvarchar(20)) -- set this to your maximum size string 
AS 
BEGIN 
    DECLARE  @index INT, 
       @start_index INT, 
       @id nvarchar(20) -- set this to your maximum size string 

    SELECT @index = 1 
    SELECT @start_index = 1 
    WHILE @index <= DATALENGTH(@list) 
    BEGIN 

     IF SUBSTRING(@list,@index,1) = ',' 
     BEGIN 

       SELECT @id = SUBSTRING(@list, @start_index, @index - @start_index) 
       INSERT @list_table ([id]) VALUES (@id) 
       SELECT @start_index = @index + 1 
     END 
     SELECT @index = @index + 1 
    END 
    SELECT @id = SUBSTRING(@list, @start_index, @index - @start_index) 
    INSERT @list_table ([id]) VALUES (@id) 
    RETURN 
END 

Ensuite, en utilisant votre resultset, vous pouvez vous joindre aux tables que vous souhaitez interroger les correspondances, et retourner un jeu de résultats de matches.

0

Si vous recherchez une boucle For Each dans SQL, la plupart des dialectes SQL ont des curseurs qui vous permettent de sélectionner tous les enregistrements en utilisant une requête, puis de parcourir les enregistrements en utilisant un curseur.

+0

True, mais comme plusieurs réponses l'ont indiqué, il est généralement préférable d'utiliser les fonctionnalités basées sur l'ensemble de la base de données plutôt que des curseurs. –

0

Pour utiliser la recherche de texte intégral, vous devez utiliser le mot-clé CONTAINS. Regardez comment l'utiliser dans les livres en ligne (je dois aller à une réunion ou donner un exemple).

+0

Oui, j'ai la requête en utilisant ContainsTable. Je regarderai le code demain encore, ai reçu des trucs de client ce soir; – ddd

1

Voici une fonction split qui a un paramètre optionnel d'un delimiter ...

CREATE FUNCTION [dbo].[fnSplit](@List VARCHAR(4000), @Delimiter CHAR(1) = ',') 
    RETURNS @Result TABLE (item VARCHAR(100)) 
    BEGIN 
     DECLARE @Item VARCHAR(100) 
     WHILE CHARINDEX(@Delimiter,@List,0) <> 0 
     BEGIN 
      SELECT @Item = SUBSTRING(@List,1,CHARINDEX(@Delimiter,@List,0)-1) 
       , @List = SUBSTRING(@List,CHARINDEX(@Delimiter,@List,0)+1,LEN(@List)) 

      IF LEN(@Item) > 0 
       INSERT INTO @Result 
        SELECT @Item 
     END 
     IF LEN(@List) > 0 
      INSERT INTO @Result 
       SELECT @List 
     RETURN 
    END 
GO 

Ensuite, votre requête pourrait ressembler à quelque chose comme ça ...

SELECT DISTINCT 
     'restaurants' AS [source] 
    , r.ID AS [ID] --Assuming ID is your primary key column 
    , r.name + '(' + r.city + ', ' + r.state + ')' AS [Description] 
FROM restaurants AS [r] 
JOIN [dbo].[fnSplit](@Query,' ') AS [terms] 
ON 
    ISNULL(r.name,'') 
    +','+ISNULL(r.city,'') 
    +','+ISNULL(r.postcode,'') LIKE '%'+terms.item+'%' 

UNION SELECT DISTINCT 
     'cuisine' AS [source] 
    , r.ID AS [ID] --Assuming ID is your primary key column 
    , r.name AS [Description] 
FROM cuisine AS [r] 
JOIN [dbo].[fnSplit](@Query,' ') AS [terms] 
ON 
    ISNULL(r.name,'') LIKE '%'+terms.item+'%' 

Le fnSplit fonction casse vos termes de requête en lignes dans une variable de table et les renvoie. Les requêtes select se joignent ensuite à la table résultante. La requête est distincte de sorte qu'une seule instance d'une ligne est renvoyée, quel que soit le nombre de termes correspondants dans la requête. La condition de jointure peut facilement être divisée en une série de conditions ET/OU, mais je pense que les opérations LIKE sont plus coûteuses que la concaténation, donc je ne fais que concaténer les colonnes.

MISE À JOUR

La requête simplifiée suivante peut être utilisée car l'indexation en texte intégral est activé.

DECLARE @Phrase VARCHAR(4000) 
SELECT @Phrase = item + '" OR "' FROM [dbo].[fnSplit](@Query,' ') 
SET @Phrase = SUBSTRING(@Phrase,0,LEN(@Phrase)-6) 


SELECT DISTINCT 
     'restaurants' AS [source] 
    , r.ID AS [ID] --Assuming ID is your primary key column 
    , r.name + '(' + r.city + ', ' + r.state + ')' AS [Description] 
FROM restaurants AS [r] 
WHERE 
    CONTAINS(r.name,@Phrase) 
    OR CONTAINS(r.city,@Phrase) 
    OR CONTAINS(r.postcode,@Phrase) 

UNION SELECT DISTINCT 
     'cuisine' AS [source] 
    , r.ID AS [ID] --Assuming ID is your primary key column 
    , r.name AS [Description] 
FROM cuisine AS [r] 
WHERE CONTAINS(r.name,@Phrase) 
Questions connexes