2009-07-08 8 views
0

Ich habe wieder mit SQL mit meiner Suchfunktion zu kämpfen.SQL-Algorithmus Trouble-for-Schleife zum Hinzufügen und Filtern von Ergebnissen

Grundsätzlich möchte ich eine gespeicherte Prozedur, die eine Zeichenfolge in Wörter aufteilt dann für jedes Wort, das ich überprüfen möchte mit Volltextsuche, wenn es in einer von 2 Tabellen ist.

Wenn Ergebnisse in diesen Tabellen gefunden werden, sollten sie sie zu allen Ergebnissen hinzufügen, die für die anderen Wörter gefunden wurden, und eine Menge zurückgeben, in der sich der Datensatz in diesen beiden Sätzen befindet.

So, hier ist mein Algorithmus

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 

Ich habe keine Ahnung, wo damit zu beginnen, und ich habe Google für Alter gesucht, aber ein begginer in SQL wobei ich einige Begriffe fehlen.

Ist dies auch der beste Weg, um so etwas zu tun?

Schätzen Sie jede Hilfe.

Bearbeiten: Nur um mehr Informationen zu geben. Stadt, Postleitzahl und Name sind alle nvarchar Felder in einem Restauranttabelle. Name der Küche ist in einer anderen Tabelle und ist durch einen anderen Tisch mit Restaurants verbunden, da ein Restaurant viele Arten von Küche servieren kann.

Alle diese Felder verfügen über eine Volltextsuche.

+0

Sie wollen "UND" oder "ODER"? Sie geben "UND" an, aber das würde bedeuten, dass das Suchwort in den Feldern Restaurant, Küche, Stadt und Postleitzahl stehen müsste. Also für alle diese Restaurants namens "21201" in "21201", MD (Postleitzahl "21201"), die "21201" zum Abendessen servieren, wäre das genau richtig ... – GalacticCowboy

+0

Sie brauchen nicht wirklich Volltext für Postleitzahlen und einfacher alter Index wird funktionieren. Außerdem verwenden Sie Volltext für etwas, für das es nicht vorgesehen war. Es wurde entwickelt, um große Textabschnitte zu indizieren, nicht für 2,3 Wörter. –

+0

Ich möchte zuerst überprüfen, ob das Wort im Restaurantnamen oder in der Küche ist (so heißt das Restaurant zum Beispiel Best Chinese). Wenn kein Match vorhanden ist, möchte ich überprüfen, ob das Wort in der Stadt ist wenn es in der Postleitzahl ist. Dies ist so die Ergebnisse gefiltert werden, wie der Benutzer Wörter in ein Textfeld eingibt. – ddd

Antwort

1

Das ist nicht wirklich die Art, wie SQL arbeitet. Sie können etwas von der gleichen Funktionalität bekommen, aber es sieht nicht so aus wie das, was Sie fragen; Sie schreiben (natürlich genug) in einem mehr prozeduralen Stil.

Zum einen versucht "add results", (vermutlich) disparate Dinge zusammen in eine einzige Sammlung zu bringen. Sie sind - ich denke - nach Restaurants und Küchen und Städten und Postleitzahlen gefragt, die alle in die gleiche Sammlung gehören, und die einzige Art von Sammlung SQL hat (grob gesagt) Tabellen, und innerhalb einer Tabelle haben alle Zeilen den gleichen Typ.

Aber vielleicht Restaurants und Küche und Stadt und Postleitzahl sind alle Felder der gleichen Tabelle? Nun, dann könnte man für ein gegebenes Wort sagen

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

Es beginnt kompliziert zu werden, wenn Sie mehrere Wörter übereinstimmen möchten; diese Antwort ist als Ausgangspunkt gedacht; Vielleicht können Sie, sobald Sie mit SQL besser vertraut sind, einfachere Fragen stellen. Viel Glück!

aktualisiert basierend auf dem Tisch Beschreibungen

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 + "%"; 

Und keine Sorge über die „ersten“ zeichnet zurückkommen. Ihre Datenbank wird schnell genug sein, dass das kein Problem sein sollte.

+0

Das ist etwas ähnlich wie das, was ich vorher hatte, aber die Sache, die ich versuche zu tun, ist die Übereinstimmung mehrerer Wörter in beliebiger Reihenfolge. Daher die Reihenfolge meiner if-Anweisungen in meinem Pseudocode. Küche ist das einzige Feld in einer anderen Tabelle, da es durch eine andere Tabelle verbunden ist, da ein Restaurant viele Arten von Küche servieren kann – ddd

0

Selbst wenn ich wollte, könnte ich Ihnen nicht die ganze Geschichte dazu sagen - Sie haben die Tabellendefinitionen oder irgendwelche Beispieldaten nicht gepostet.

Das sagte, werde ich ein paar Vermutungen machen. Ich denke vor allem, dass Sie SQL in einer Satz-basierten Weise verwenden müssen. Denken Sie nicht an die Verarbeitung von Zeile für Zeile - verarbeiten Sie eine Menge. Dann kombinieren Sie die Sets zusammen.

+0

Ich habe meine Frage aktualisiert, um weitere Informationen auf den Tischen zu enthalten. – ddd

1

Es gibt eine Aufteilung in Wörter function here.Welche gibt eine Tabelle zurück, an der Sie teilnehmen können?

Sie sollten auch metaphone oder soundex untersuchen, aber um das zu unterstützen, müssen Sie Ihre Daten in einer separaten Tabelle vorindizieren oder die Methaphon- oder Soundex-Codes für jedes Wort in einem separaten Feld vorberechnen und ebenfalls indexieren .

1

Es sieht so aus, als ob Sie versuchen, einen prozeduralen Ansatz für eine satzbasierte deklarative Abfragesprache anzuwenden.

Als Erstes können Sie eine Zeichenfolge in eine Ergebnismenge aufteilen, indem Sie eine tabellenwertige benutzerdefinierte Funktion verwenden. So etwas wie die folgenden tun -

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 

Dann wird Ihr resultset verwenden, können Sie in den Tabellen verbinden, die Sie wünschen für die Spiele zur Abfrage und geben einen resultset Streichhölzer.

0

Wenn Sie nach einer For Each-Schleife in SQL suchen, haben die meisten SQL-Dialekte Cursors, mit denen Sie alle Datensätze mit einer Abfrage auswählen und dann mit einem Cursor über die Datensätze iterieren können.

+0

Richtig, aber wie mehrere Antworten angeben, ist es normalerweise besser, die set-basierten Funktionen der Datenbank anstelle von Cursorn zu verwenden. –

0

Um die Volltextsuche zu verwenden, möchten Sie das CONTAINS-Schlüsselwort verwenden. Schlagen Sie nach, wie Sie es in Büchern online verwenden können (ich muss zu einer Besprechung oder ich gebe ein Beispiel).

+0

Ja, ich habe die Abfrage mit ContainsTable. Ich werde mir den Code morgen noch einmal ansehen, habe heute Abend Client-Sachen; – ddd

1

Hier ist eine Split-Funktion, die einen optionalen Parameter eines Trennzeichen hat ...

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 

Dann ist Ihre Abfrage so etwas wie dieses ...

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+'%' 

Die fnsplit Funktion aussehen könnte bricht die Suchbegriffe in Zeilen in einer Tabellenvariablen und gibt sie zurück. Die Select-Abfragen werden dann mit der resultierenden Tabelle verknüpft. Die Abfrage ist eindeutig, sodass nur 1 Instanz einer Zeile zurückgegeben wird, unabhängig davon, wie viele Begriffe in der Abfrage übereinstimmen. Die Join-Bedingung könnte leicht in eine Reihe von UND/ODER-Bedingungen zerlegt werden, aber ich denke, LIKE-Operationen sind teurer als Verkettungen, so dass ich nur die Spalten verkette.

UPDATE

Die folgende vereinfachte Abfrage kann, da aktiviert ist Volltext-Indizierung verwendet werden.

Verwandte Themen