Ich habe vier Spalten mit Namen und möchte diese mit einer LIKE
in einer Microsoft SQL Server-Umgebung durchsuchen .
Die Komplikation kommt , dass Namen kann links und rechts Apostrophe / gewinkelt Apostrophe (dh umfassen ‘
und ’
, char(145)
und char(146)
jeweils), die einen geraden Apostroph übereinstimmen sollte (dh '
, char(39)
)
Folgendes zu tun ist sehr langsam:
SELECT person_id
FROM person
WHERE REPLACE(
REPLACE(
person_name,
CHAR(145),
CHAR(39)
),
CHAR(146),
CHAR(39)
) LIKE '{USER_INPUT}'
Wie in der SQL-Ersetzungsanweisung erläutert , REPLACE
die beim Stapelüberlauf zu langsam ist , liegt dies daran, dass die Verwendung von die Anweisung nicht aufladbar macht.
Gibt es eine Möglichkeit, wie SQL Server mit solchen Situationen besser umgehen kann?
Eine Lösung , die vorgeschlagen wurde , ist die Anwendung zu haben , erzeugt einen ‚suchbar‘ Wert, der alle Felder verkettet ( person_name
, person_surname
, person_nickname
, etc.) und wandelt die problematischen Zeichen an der Stelle der Bearbeitung. Dies könnte effektiv indiziert und durchsucht werden. Das Speichern dieser Daten in einer separaten SQL-Tabelle / -Spalte würde weniger Umschreiben der Anwendung erfordern als das Implementieren einer vollständigen NoSQL-Lösung wie Lucene.
Das obige Beispiel ist eine Vereinfachung: Die Abfrage wird nicht wie oben erläutert buchstäblich erstellt, und wir implementieren SQL-Injection-Schutz (und andere).
Die Frage ist, wie die abgewinkelten Apostrophe in den Tabellendaten durch gerade ersetzt werden können. Zu klären:
- Benutzerbedarf
O‘Malley
- dies sollte mit beidenO‘Malley
oder übereinstimmenO'Malley
- Benutzerbedarf
O'Malley
- dies sollte mit beidenO‘Malley
oder übereinstimmenO'Malley
Wir müssen die SQL-Daten ersetzen, nicht die Benutzereingaben. Wir können die Benutzereingaben auf dem Weg durch die Anwendung so konvertieren, dass wir sie in einfache Apostrophe ändern, wenn sie abgewinkelte Apostrophe eingeben, bevor wir sie an SQL übergeben. Es sind die Daten in SQL, die wir standardisieren müssen.
Leider müssen die Daten als korrekte abgewinkelte Klammer in der Datenbank verbleiben, aber wenn wir die Suche durchführen, müssen wir sie alle mit geraden Apostrophen vergleichen.
@user_input
direkt vor der tatsächlichen ansehen,SELECT
sehen Sie die Zeichen zwischen den[]
's. Das bedeutet, dass dieser bestimmte Punkt mit einem der Zeichen zwischen ihnen übereinstimmt.[‘’']
- für denLIKE
Operator. Daher spielt es keine Rolle, was der Benutzer übergibt, selbst wenn er mehr als einen Typ in einem Suchbegriff verwendet, da jeder mit einer der drei in den Daten übereinstimmt.Ich hätte schwören können, dass ich gesehen habe, wie diese Zeichen irgendwo gleichgesetzt wurden, aber jetzt kann ich sie nicht finden. Ich habe alle Kollatierungen in SQL Server 2012 und 2014 überprüft, und keine davon entspricht
CHAR(39)
einer der beiden anderen. Vergessen Sie also diese ursprüngliche Idee.Eine Option, wenn die genaue Art des Apostrophs nicht von besonderer Bedeutung ist, besteht darin, nur die Daten zu aktualisieren:
... umwandeln
CHAR(145)
undCHAR(146)
inCHAR(39)
. Dann müssen Sie nichts programmgesteuert tun. Sie müssen nur gelegentlich die Daten überprüfen oder einen Auslöser erstellen, um diese inCHAR(39)
onINSERT
oder zu übersetzenUPDATE
.quelle
Seien Sie vorsichtig: Sind Sie sicher, dass Sie dort wirklich Unicode haben und keine 8-Bit-Codepage? Während 145 in vielen Schriftarten als ziemlich angezeigt wird, ist dies im Unicode-Standard nicht offiziell (145 und 146 sind als PRIVATE USE ONE bzw. PRIVATE USE TWO aufgeführt: http://www.fileformat.info/info/unicode/). char / 0091 / index.htm ). Wenn Sie 8-Bit-Text haben, kann dieses Zeichen zu etwas anderem in einer anderen Codepage werden (145 ist in einigen Fällen æ), sodass Sie möglicherweise Probleme mit der Zeichenübersetzung in Ihrer Anwendung haben. Geschweifte einfache Anführungszeichen sind 2018 und 2019 in Unicode.
Abgesehen von solchen möglichen Codierungsproblemen: Wenn Sie eine Funktion um die gesuchte Spalte wickeln, wird der Filter nicht aufladbar, wie Sie bereits festgestellt haben. In diesem Fall ist drei sehr wenig, was Sie ohne strukturelle Änderungen dagegen tun können.
Wenn Sie einen Index haben
person_name
, können Sie hoffentlich einen Tabellenscan in einen Indexscan umwandeln (obwohl dies in Abhängigkeit von anderen Filtern, einschließlich der durch Joins usw. implizierten, in der Abfrage möglicherweise nicht ganz so einfach ist). .Wenn Sie die Möglichkeit haben, das Tabellenlayout sowie den Abfragecode zu steuern, können Sie eine persistierte berechnete Spalte erstellen, die eine "kanonische" Zeichenfolge enthält (in diesem Fall die Eine kanonische Darstellung würde durch so etwas wie
person_name_canon = REPLACE(REPLACE(person_name, CHAR(145), CHAR(39)), CHAR(146), CHAR(39)))
) erzeugt werden. Stellen Sie sicher, dass die Spalte an relevanten Indizes beteiligt ist, und wenn Sie nach dieser Spalte suchen, können Sie dies tunWHERE person_name_canon LIKE REPLACE(REPLACE(@user_input, CHAR(145), CHAR(39)), CHAR(146), CHAR(39)))
.Wenn Sie die berechnete Spalte nicht hinzufügen können, aber ein nützlicher Index für
person_name
einen leicht hackigen Ansatz vorhanden ist, können Sie Folgendes tun:Bitte beachten Sie, dass ich dies überhaupt nicht getestet habe, damit es möglicherweise nicht den gewünschten Effekt hat, aber theoretisch sollte die zweite Klausel zu einem sargable Teilfilter führen. Wenn also @user_input ist
Michael O'Brien
, kann der Index verwendet werden, um Text zu finden, der mitMichael O
dem anderen beginnt Der Filter wirft diejenigen aus, die nicht vollständig übereinstimmen, nachdem alle Ersetzungen der gespeicherten Daten abgeschlossen sind.Möglicherweise müssen Sie auch die verschiedenen Anführungszeichen in der Eingabe berücksichtigen, wenn dies in Ihrer Geschäftslogik vor der Suche nicht normalisiert wird.
quelle
CHAR(145)
nicht verwendetNCHAR(145)
und die Frage als derzeit nicht potenziell langsam eingestuft wird. 2) Auch wenn die tatsächlichen Daten NVARCHAR sind,CHAR(145)
funktioniert die Verwendung so, wie sie implizit in konvertiert wirdNCHAR(8216)
. 3) Eine persistierte berechnete Spalte ist eine völlig unnötige Verschwendung von Speicherplatz sowohl auf der Festplatte als auch im Speicher, da Sie mit einemLIKE
verwendeten Platzhalter eine Indexsuche durchführen können, solange das Muster nicht mit einem beginnt . 4) Es gibt keinen Grund, einen der beiden Hacks durchzuführen, da Kenneths Antwort alle Fälle behandelt.WHERE FUNCTION({column}) = {input}
) nicht auf der anderen Seite des Vergleichs stehen