Wir entwickeln eine Suche als Teil eines größeren Systems.
Wir haben Microsoft SQL Server 2014 - 12.0.2000.8 (X64) Standard Edition (64-bit)
mit diesem Setup:
CREATE TABLE NewCompanies(
[Id] [uniqueidentifier] NOT NULL,
[Name] [nvarchar](400) NOT NULL,
[Phone] [nvarchar](max) NULL,
[Email] [nvarchar](max) NULL,
[Contacts1] [nvarchar](max) NULL,
[Contacts2] [nvarchar](max) NULL,
[Contacts3] [nvarchar](max) NULL,
[Contacts4] [nvarchar](max) NULL,
[Address] [nvarchar](max) NULL,
CONSTRAINT PK_Id PRIMARY KEY (Id)
);
Phone
ist eine strukturierte durch Kommas getrennte Ziffernfolge wie"77777777777, 88888888888"
Email
ist eine strukturierte E-Mail-Zeichenfolge mit Kommas wie"[email protected], [email protected]"
(oder ohne Kommas wie"[email protected]"
)Contacts1, Contacts2, Contacts3, Contacts4
sind Textfelder, in denen Benutzer Kontaktdaten in freier Form angeben können. Wie"John Smith +1 202 555 0156"
oder"Bob, +1-999-888-0156, [email protected]"
. Diese Felder können E-Mails und Telefone enthalten, nach denen wir weiter suchen möchten.
Hier erstellen wir Volltextmaterial
-- FULL TEXT SEARCH
CREATE FULLTEXT CATALOG NewCompanySearch AS DEFAULT;
CREATE FULLTEXT INDEX ON NewCompanies(Name, Phone, Email, Contacts1, Contacts2, Contacts3, Contacts4, Address)
KEY INDEX PK_Id
Hier ist ein Datenbeispiel
INSERT INTO NewCompanies(Id, Name, Phone, Email, Contacts1, Contacts2, Contacts3, Contacts4)
VALUES ('7BA05F18-1337-4AFB-80D9-00001A777E4F', 'PJSC Azimuth', '79001002030, 78005005044', '[email protected], [email protected]', 'John Smith', 'Call only at weekends +7-999-666-22-11', NULL, NULL)
Tatsächlich haben wir ungefähr 100.000 solcher Aufzeichnungen.
Wir erwarten, dass Benutzer einen Teil der E-Mail wie "@ gmail.com" angeben können. Dies sollte alle Zeilen mit Google Mail-E-Mail-Adressen in einem der Email, Contacts1, Contacts2, Contacts3, Contacts4
Felder zurückgeben.
Gleiches gilt für Telefonnummern. Benutzer können nach einem Muster wie "70283" suchen, und eine Abfrage sollte Telefone mit diesen Ziffern zurückgeben. Es ist sogar für Freiformfelder, Contacts1, Contacts2, Contacts3, Contacts4
in denen wir wahrscheinlich zuerst alle außer Ziffern und Leerzeichen entfernen sollten, bevor wir suchen.
Früher haben LIKE
wir für die Suche verwendet, als wir ungefähr 1500 Datensätze hatten und es hat gut funktioniert, aber jetzt haben wir viele Datensätze und die LIKE
Suche dauert unendlich, um Ergebnisse zu erhalten.
So versuchen wir, Daten von dort zu erhalten:
SELECT * FROM NewCompanies WHERE CONTAINS((Email, Contacts1, Contacts2, Contacts3, Contacts4), '"[email protected]*"') -- this doesn't get the row
SELECT * FROM NewCompanies WHERE CONTAINS((Phone, Contacts1, Contacts2, Contacts3, Contacts4), '"6662211*"') -- doesn't get anything
SELECT * FROM NewCompanies WHERE CONTAINS(Name, '"zimuth*"') -- doesn't get anything
nvarchar(MAX)
hier? Ich habe noch nie von jemandem gehört oder jemanden getroffen, dessen Name 1 Milliarde Zeichen lang ist. Laut dieser Antwort darf eine E-Mail-Adresse nicht länger als 254 Zeichen sein. Sie haben dort also auch 1 Milliarde ~ verschwendete Charaktere.@gmail.com
als Suchbegriff verwendet wird, da der@
Charakter ein Wortbrecher ist. Mit anderen Worten, je die Version von SQL Server Sie haben, Worte im Index für[email protected]
werden entweder (A)user
,gmail
undcom
oder (B)user
,[email protected]
,gmail
undcom
. REF: Verhaltensänderungen bei der Volltextsuche.
.SELECT * FROM NewCompanies WHERE Id IN (SELECT ID from .... where MyOuterApply.EmailCol1 LIKE '%'+@SearchString+'%') OR Id IN (SELECT ID from .... where MyOuterApply.EmailCol2 LIKE '%'+@SearchString+'%')
Antworten:
Eigentlich Anfragen
gegen
'Call only at weekends +7-999-666-22-11'
undgegen
'PJSC Azimuth'
Sie funktionieren wie erwartet .
Siehe Präfixbegriff . Weil
6662211*
ist kein Präfix von+7-999-666-22-11
sowiezimuth*
ist kein Präfix vonAzimuth
Wie für
Dies ist wahrscheinlich auf Wortbrecher zurückzuführen , wie in Kommentaren immer erwähnt wurde. Siehe Wortbrecher
Ich denke nicht, dass die Volltextsuche für Ihre Aufgabe geeignet ist.
Warum für FTS genau dieselben Aufgaben verwenden, für die der LIKE-Operator verwendet wird? Wenn es einen besseren Indextyp für LIKE-Abfragen gäbe ... dann gäbe es den besseren Indextyp , nicht die völlig andere Technologie und Syntax.
Und in keiner Weise wird es Ihnen helfen,
"6662211*"
gegen "666 irgendein beliebiges Zeichen 22 etwas beliebiges Zeichen 11" abzugleichen .Bei der Volltextsuche geht es nicht um Regex-es (und es
"6662211*"
ist nicht einmal ein korrekter Ausdruck für den Job - es gibt nichts über "irgendeinen beliebigen Zeichen" -Teil), sondern um Synonyme, Wortformen usw.Aber ist es überhaupt möglich, effektiv nach Teilzeichenfolgen zu suchen?
Ja, so ist es. Was können wir tun, wenn wir potenzielle Kunden wie das Schreiben einer eigenen Suchmaschine außer Acht lassen
SQL
?Zunächst einmal - es ist unbedingt erforderlich, Ihre Daten zu bereinigen! Wenn Sie den Benutzern die genauen Zeichenfolgen zurückgeben möchten, die sie eingegeben haben
... Sie können sie so speichern, wie sie sind ... und sie mitnehmen.
Dann müssen Sie Daten aus dem Freiformtext extrahieren (es ist nicht so schwer für E-Mails und Telefonnummern) und die Daten in einer kanonischen Form speichern. Für E-Mails ist das einzige, was Sie wirklich tun müssen - machen Sie sie alle in Klein- oder Großbuchstaben (spielt keine Rolle) und teilen Sie sie dann vielleicht beim
@
Singen auf. Aber in Telefonnummern müssen Sie nur Ziffern hinterlassen(... und dann können Sie sie sogar als Nummern speichern . Das kann Ihnen Platz und Zeit sparen. Aber die Suche wird anders sein ... Lassen Sie uns jetzt in eine einfachere eintauchen und universelle Lösung mit Strings.)
Wie MatthewBaker erwähnt hat, können Sie eine Tabelle mit Suffixen erstellen. Dann können Sie so suchen
Sie sollten den Platzhalter
%
nur am Ende platzieren . Oder die Suffixe-Tabelle bietet keine Vorteile.Nehmen wir zum Beispiel eine Telefonnummer
Nachdem wir Abfallzeichen entfernt haben, wird es 11 Ziffern haben. Das heißt, wir benötigen 11 Suffixe für eine Telefonnummer
Die räumliche Komplexität für diese Lösung ist also linear ... nicht so schlimm, würde ich sagen ... Aber warten Sie, es ist die Komplexität in der Anzahl der Datensätze. Aber in Symbolen ... brauchen wir
N(N+1)/2
Symbole, um alle Suffixe zu speichern - das ist quadratische Komplexität ... nicht gut ... aber wenn Sie jetzt100 000
Aufzeichnungen haben und in naher Zukunft keine Pläne für Millionen haben - können Sie damit weitermachen Lösung.Können wir die Raumkomplexität reduzieren?
Ich werde nur die Idee beschreiben, deren Umsetzung einige Anstrengungen erfordern wird. Und wahrscheinlich müssen wir die Grenzen von überschreiten
SQL
Angenommen, Sie haben 2 Zeilen
NewCompanies
und 2 Zeichenfolgen Freiformtext:Wie groß sollte der Suffix-Tisch sein? Offensichtlich brauchen wir nur 2 Datensätze.
Nehmen wir ein anderes Beispiel. Auch 2 Zeilen, 2 freie Textzeichenfolgen zu suchen. Aber jetzt ist es:
Mal sehen, wie viele Suffixe wir jetzt brauchen:
Nein, so schlecht, aber auch nicht so gut.
Was können wir sonst noch tun?
Angenommen, der Benutzer gibt
"c11"
das Suchfeld ein. DannLIKE 'c11%'
braucht ‚ c11 cc‘ Suffix erfolgreich zu sein. Aber wenn"c11"
wir nicht suchen"c%"
, sondern zuerst suchen , dann nach"c1%"
und so weiter? Die erste Suche ergibt nur eine Zeile vonNewCompanies
. Und es würde keine Notwendigkeit für nachfolgende Suchen geben. Und wir könnenund wir haben nur 4 Suffixe
Ich kann nicht sagen, wie hoch die Raumkomplexität in diesem Fall sein würde, aber es scheint akzeptabel zu sein.
quelle
In solchen Fällen ist die Volltextsuche nicht ideal. Ich war im selben Boot wie Sie. Ebenso ist die Suche zu langsam und die Volltextsuche sucht nach Wörtern, die mit einem Begriff beginnen und keinen Begriff enthalten.
Wir haben verschiedene Lösungen ausprobiert. Eine reine SQL-Option besteht darin, eine eigene Version der Volltextsuche zu erstellen, insbesondere eine invertierte Indexsuche. Wir haben es versucht und es war erfolgreich, hat aber viel Platz in Anspruch genommen. Wir haben eine sekundäre Haltetabelle für teilweise Suchbegriffe erstellt und die Volltextindizierung verwendet. Dies bedeutet jedoch, dass wir wiederholt mehrere Kopien derselben Sache gespeichert haben. Zum Beispiel haben wir "longword" als Longword, ongword, ngword, gword ... usw. gespeichert. Jede enthaltene Phrase steht also immer am Anfang des indizierten Begriffs. Eine schreckliche Lösung voller Fehler, aber es hat funktioniert.
Wir haben uns dann angesehen, einen separaten Server für Lookups zu hosten. Wenn Sie Lucene und elastisearch googeln, erhalten Sie gute Informationen zu diesen Standardverpackungen.
Schließlich haben wir unsere eigene Suchmaschine entwickelt, die neben SQL läuft. Dies hat es uns ermöglicht, phonetische Suchen (Doppelmetaphon) zu implementieren und dann Levenshtein-Berechnungen neben Soundex zu verwenden, um die Relevanz festzustellen. Overkill für viele Lösungen, aber die Mühe lohnt sich in unserem Anwendungsfall. Wir haben sogar jetzt die Möglichkeit, Nvidia-GPUs für die Cuda-Suche zu nutzen, aber dies war eine ganz neue Reihe von Kopfschmerzen und schlaflosen Nächten. Die Relevanz all dieser Faktoren hängt davon ab, wie oft Ihre Suchvorgänge ausgeführt werden und wie reaktiv Sie sie benötigen.
quelle
Volltextindizes weisen eine Reihe von Einschränkungen auf. Sie können Platzhalter für Wörter verwenden, bei denen der Index feststellt, dass sie ganze "Teile" sind, aber selbst dann sind Sie auf den Endteil des Wortes beschränkt. Deshalb können Sie
CONTAINS(Name, '"Azimut*"')
aber nicht verwendenCONTAINS(Name, '"zimuth*"')
Aus der Microsoft- Dokumentation :
Die Punkte in der E-Mail, wie im Titel angegeben, sind nicht das Hauptproblem. Dies funktioniert zum Beispiel:
In diesem Fall identifiziert der Index die gesamte E-Mail-Zeichenfolge als gültig sowie "gmail" und "gmail.com". Nur "SMS" ist jedoch nicht gültig.
Das letzte Beispiel ist ähnlich. Die Teile der Telefonnummer sind indiziert (z. B. 666-22-11 und 999-666-22-11), aber das Entfernen der Bindestriche ist keine Zeichenfolge, über die der Index Bescheid wissen wird. Ansonsten funktioniert das:
quelle