Leere SQL Server 2008-Zeichenfolge im Vergleich zum Leerzeichen


82

Ich bin heute Morgen auf etwas Seltsames gestoßen und dachte, ich würde es zur Kommentierung einreichen.

Kann jemand erklären, warum die folgende SQL-Abfrage "gleich" gedruckt wird, wenn sie mit SQL 2008 ausgeführt wird. Die Datenbankkompatibilitätsstufe ist auf 100 festgelegt.

if '' = ' '
    print 'equal'
else
    print 'not equal'

Und das gibt 0 zurück:

select (LEN(' '))

Es scheint, als würde der Raum automatisch beschnitten. Ich habe keine Ahnung, ob dies in früheren Versionen von SQL Server der Fall war, und ich habe keine Möglichkeit mehr, es überhaupt zu testen.

Ich bin darauf gestoßen, weil eine Produktionsabfrage falsche Ergebnisse lieferte. Ich kann dieses Verhalten nirgendwo dokumentiert finden.

Hat jemand irgendwelche Informationen dazu?


2
SQL 2005: select len ​​('') gibt 0
Mayo

1
Das gleiche gilt für SQL Server 2000.
Pierre-Alain Vigeant

1
Das ist eine faszinierende Frage. Es scheint gleich zurückzugeben, egal wie viele Leerzeichen Sie in eine der Zeichenfolgen einfügen, unabhängig davon, ob sie übereinstimmen oder nicht. Nach weiteren Experimenten stellte ich fest, dass vor dem Vergleich auf beiden Seiten des Gleichheitsoperators effektiv ein RTRIM durchgeführt wird. Es sieht so aus, als hätten Sie eine Antwort auf die LEN-Funktion erhalten, aber ich bin wirklich an einer gründlicheren Antwort interessiert als "Varchare und Gleichheit sind in TSQ schwierig" für den Gleichheitsteil Ihrer Frage.
JohnFx

Oracle macht das auch, glaube ich.
Federbrecher

Im Allgemeinen finde ich, dass das Speichern leerer Zeichenfolgen eine schlechte Idee ist, und dies ist einer der Gründe. Ich bevorzuge die Verwendung von Null und finde viele Probleme, wenn Leute versuchen, Nullinformationen in einen Wert wie eine leere Zeichenfolge oder einen Datenweg außerhalb des normalen Bereichs zu verwandeln.
HLGEM

Antworten:


87

varchars und Gleichheit sind in TSQL heikel. Die LENFunktion sagt:

Gibt die Anzahl der Zeichen und nicht die Anzahl der Bytes des angegebenen Zeichenfolgenausdrucks zurück, ausgenommen nachgestellte Leerzeichen .

Sie müssen verwenden DATALENGTH, um eine echte byteAnzahl der fraglichen Daten zu erhalten. Wenn Sie Unicode-Daten haben, beachten Sie, dass der Wert, den Sie in dieser Situation erhalten, nicht der Länge des Textes entspricht.

print(DATALENGTH(' ')) --1
print(LEN(' '))        --0

Wenn es um die Gleichheit von Ausdrücken geht, werden die beiden Zeichenfolgen auf folgende Gleichheit verglichen:

  • Kürzere Zeichenfolge abrufen
  • Mit Leerzeichen auffüllen, bis die Länge der einer längeren Saite entspricht
  • Vergleichen Sie die beiden

Dies ist der mittlere Schritt, der zu unerwarteten Ergebnissen führt. Nach diesem Schritt vergleichen Sie Leerzeichen effektiv mit Leerzeichen. Daher werden sie als gleich angesehen.

LIKEverhält sich besser als =in der Situation "Leerzeichen", da das Muster, mit dem Sie übereinstimmen wollten, nicht leer aufgefüllt wird:

if '' = ' '
print 'eq'
else
print 'ne'

Wird geben eqwährend:

if '' LIKE ' '
print 'eq'
else
print 'ne'

Wird geben ne

Vorsicht LIKE: Es ist nicht symmetrisch: Es behandelt nachgestellte Leerzeichen als signifikant im Muster (RHS), nicht jedoch im Übereinstimmungsausdruck (LHS). Folgendes wird von hier genommen :

declare @Space nvarchar(10)
declare @Space2 nvarchar(10)

set @Space = ''
set @Space2 = ' '

if @Space like @Space2
print '@Space Like @Space2'
else
print '@Space Not Like @Space2'

if @Space2 like @Space
print '@Space2 Like @Space'
else
print '@Space2 Not Like @Space'

@Space Not Like @Space2
@Space2 Like @Space

1
Gute Antwort. Das hatte ich in der LEN-Dokumentation nicht bemerkt. Es ist jedoch nicht auf LEN beschränkt. Die Funktion RECHTS und LINKS zeigt ein ähnliches Verhalten, ist dort jedoch nicht dokumentiert. Es scheint das Wörtliche mit einem Leerzeichen zu sein, das das Problem verursacht. Mir ist aufgefallen, dass dies auch gleich zurückgibt: Wenn '' = SPACE (1) print 'gleich', sonst print 'ungleich' Ich bin nicht wirklich daran interessiert, die wahre Länge zu erhalten, ich war nur verwirrt, warum, als ich nach einem Leerzeichen in suchte In einer Spalte wurden alle Spalten zurückgegeben, bei denen es sich um leere Zeichenfolgen handelte.
Jhale

Auch nette Informationen über die LIKE-Anweisung. Ich denke, die Moral der Geschichte ist, sich nicht in die Position zu bringen, in der Sie ein Leerzeichen und eine leere Zeichenfolge vergleichen müssen.
Jhale

2
Das Problem ist größer als der Vergleich eines Leerzeichens mit einer leeren Zeichenfolge. Der Vergleich von zwei Zeichenfolgen, die in einer unterschiedlichen Anzahl von Leerzeichen enden, zeigt dasselbe Verhalten.
JohnFx

3
@butterchicken: Entschuldigung für einen so späten Beitrag, ich habe gerade diese Frage gesehen, aber als ich diese (die letzte) auf meinem lief, sql-server-2008 r2bekomme ich , @Space Not Like @Space2 @Space2 Not Like @Space . Irgendeine Idee warum?
Razort4x

1
Das Ergebnis, das unter SQL Server 2012 und SQL Server 2014 bestätigt wurde, lautet:@Space Not Like @Space2 @Space2 Not Like @Space
Nur ein Lernender

19

Der Operator = ist T-SQL ist nicht so sehr "gleich", sondern "sind dasselbe Wort / dieselbe Phrase gemäß der Zusammenstellung des Kontextes des Ausdrucks", und LEN ist "die Anzahl der Zeichen in dem Wort / der Phrase". Keine Kollatierungen behandeln nachgestellte Leerzeichen als Teil des vorangestellten Wortes / Satzes (obwohl sie führende Leerzeichen als Teil der Zeichenfolge behandeln, der sie vorangehen).

Wenn Sie "dies" von "dies" unterscheiden müssen, sollten Sie den Operator "Sind dasselbe Wort oder dieselbe Phrase" nicht verwenden, da "Dies" und "Dies" dasselbe Wort sind.

Zu way = works trägt die Idee bei, dass der String-Equality-Operator vom Inhalt seiner Argumente und vom Kollatierungskontext des Ausdrucks abhängen sollte, aber nicht von den Argumenttypen, wenn beide Stringtypen sind .

Das natürliche Sprachkonzept von "das sind das gleiche Wort" ist normalerweise nicht präzise genug, um von einem mathematischen Operator wie = erfasst zu werden, und es gibt kein Konzept des Zeichenfolgentyps in natürlicher Sprache. Der Kontext (dh die Kollatierung) ist wichtig (und existiert in natürlicher Sprache) und ist Teil der Geschichte, und zusätzliche Eigenschaften (einige, die skurril erscheinen) sind Teil der Definition von =, um sie in der unnatürlichen Welt von gut definiert zu machen Daten.

Bei der Typproblematik möchten Sie nicht, dass sich Wörter ändern, wenn sie in verschiedenen Zeichenfolgentypen gespeichert werden. Beispielsweise können die Typen VARCHAR (10), CHAR (10) und CHAR (3) alle Darstellungen des Wortes "Katze" enthalten, und? = 'cat' sollte uns entscheiden lassen, ob ein Wert eines dieser Typen das Wort 'cat' enthält (wobei Fall- und Akzentprobleme durch die Sortierung bestimmt werden).

Antwort auf den Kommentar von JohnFx:

Siehe Verwenden von char- und varchar-Daten in Online-Büchern. Zitat von dieser Seite, Hervorhebung von mir:

Jeder char- und varchar-Datenwert hat eine Sortierung. Kollatierungen definieren Attribute wie die Bitmuster, die zur Darstellung der einzelnen Zeichen verwendet werden, Vergleichsregeln und die Empfindlichkeit gegenüber Groß- und Kleinschreibung oder Akzentuierung.

Ich bin damit einverstanden, dass es einfacher zu finden sein könnte, aber es ist dokumentiert.

Bemerkenswert ist auch, dass die Semantik von SQL, bei der = mit den realen Daten und dem Kontext des Vergleichs zu tun hat (im Gegensatz zu etwas über auf dem Computer gespeicherte Bits), seit langem Teil von SQL ist. Die Prämisse von RDBMS und SQL ist die getreue Darstellung realer Daten, daher die Unterstützung von Kollatierungen viele Jahre bevor ähnliche Ideen (wie CultureInfo) in den Bereich algolähnlicher Sprachen gelangten. Die Prämisse dieser Sprachen war (zumindest bis vor kurzem) die Problemlösung im Ingenieurwesen, nicht die Verwaltung von Geschäftsdaten. (In letzter Zeit hat die Verwendung ähnlicher Sprachen in nicht-technischen Anwendungen wie der Suche einige Fortschritte gemacht, aber Java, C # usw. haben immer noch Probleme mit ihren nicht-geschäftlichen Wurzeln.)

Meiner Meinung nach ist es nicht fair, SQL dafür zu kritisieren, dass es sich von "den meisten Programmiersprachen" unterscheidet. SQL wurde entwickelt, um ein Framework für die Modellierung von Geschäftsdaten zu unterstützen, das sich stark vom Engineering unterscheidet. Daher ist die Sprache anders (und besser für das Ziel).

Als SQL zum ersten Mal angegeben wurde, hatten einige Sprachen keinen integrierten Zeichenfolgentyp. Und in einigen Sprachen vergleicht der Gleichheitsoperator zwischen Zeichenfolgen überhaupt keine Zeichendaten, sondern Referenzen! Es würde mich nicht überraschen, wenn in ein oder zwei weiteren Jahrzehnten die Idee, dass == kulturabhängig ist, zur Norm wird.


BOL beschreibt den Operator = folgendermaßen: "Vergleicht die Gleichheit zweier Ausdrücke (ein Vergleichsoperator)." Unabhängig davon, ob das Verhalten korrekt ist oder nicht, müssen Sie zugeben, dass es in Bezug auf die Verwendung dieses Operators in den meisten Programmiersprachen äußerst verwirrend und nicht standardisiert ist. MS sollte der Dokumentation mindestens eine Warnung zu diesem Verhalten hinzufügen.
JohnFx

@ JohnFx: Siehe meine Antwort, die zu lang für einen Kommentar ist, in meiner Antwort.
Steve Kass

9

Ich habe diesen Blog-Artikel gefunden, der das Verhalten beschreibt und erklärt, warum.

Der SQL-Standard verlangt, dass Zeichenfolgenvergleiche die kürzere Zeichenfolge effektiv mit Leerzeichen auffüllen. Dies führt zu dem überraschenden Ergebnis, dass N '' = N '' (die leere Zeichenfolge entspricht einer Zeichenfolge aus einem oder mehreren Leerzeichen) und allgemeiner jede Zeichenfolge einer anderen Zeichenfolge entspricht, wenn sie sich nur durch nachgestellte Leerzeichen unterscheiden. Dies kann in einigen Kontexten ein Problem sein.

Weitere Informationen finden Sie auch in MSKB316626


Vielen Dank. Ich bin überrascht, dass es im Standard ist. Ich bin sicher, jemand, der viel schlauer ist als ich, hatte einen guten Grund dafür.
Jhale

@ John: Wolltest du ≠ (nicht gleich) in deinen Kommentar schreiben?
Steve Kass

Das ursprüngliche Zitat enthielt einen Fehler, den ich direkt kopierte. Ich habe das Zitat aktualisiert, um zu reflektieren, was der ursprüngliche Autor meinte.
JohnFx

5

Vor einiger Zeit gab es eine ähnliche Frage, bei der ich mich hier mit einem ähnlichen Problem befasst habe

LEN(' ')Verwenden DATALENGTH(' ')Sie stattdessen - das gibt Ihnen den richtigen Wert.

Die Lösungen bestanden darin, eine LIKEKlausel zu verwenden , wie in meiner Antwort dort erläutert, und / oder eine zweite Bedingung in die WHEREzu überprüfende Klausel aufzunehmenDATALENGTH ebenfalls .

Lesen Sie diese Frage und die Links dort.


3

Um einen Wert mit einem Literalraum zu vergleichen, können Sie diese Technik auch als Alternative zur LIKE-Anweisung verwenden:

IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'

0

So unterscheiden Sie Datensätze bei der Auswahl mit den Feldern char / varchar auf dem SQL Server: Beispiel:

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

erwartet

mykey (int) | myfield (varchar10)

1 | 'Daten '

erhalten

mykey | mein Feld

1 | 'Daten' 2 | 'Daten '

Selbst wenn ich schreibe select mykey, myfield from mytable where myfield = 'data'(ohne endgültiges Leerzeichen), erhalte ich die gleichen Ergebnisse.

Wie habe ich gelöst? In diesem Modus:

select mykey, myfield
from mytable
where myfield = @mayvar 
and DATALENGTH(isnull(myfield,'')) = DATALENGTH(@mayvar)

und wenn es einen Index für myfield gibt, wird dieser jeweils verwendet.

Ich hoffe es wird hilfreich sein.


0

Eine andere Möglichkeit besteht darin, es wieder in einen Zustand zu versetzen, in dem der Raum einen Wert hat. Beispiel: Ersetzen Sie das Leerzeichen durch ein Zeichen wie _

if REPLACE('hello',' ','_') = REPLACE('hello ',' ','_')
    print 'equal'
else
    print 'not equal'

Rückgabe: ungleich

Nicht ideal und wahrscheinlich langsam, aber ein weiterer schneller Weg, wenn er schnell benötigt wird.


0

Manchmal muss man sich mit Leerzeichen in Daten befassen, mit oder ohne andere Zeichen, obwohl die Idee, Null zu verwenden, besser ist - aber nicht immer verwendbar. Ich bin auf die beschriebene Situation gestoßen und habe sie folgendermaßen gelöst:

... where ('>' + @space + '<') <> ('>' + @space2 + '<')

Natürlich würden Sie das nicht für große Datenmengen tun, aber es funktioniert schnell und einfach für einige hundert Zeilen ...


1
Die Frage war, warum sich SQL Server so verhalten hat und nicht, wie man mit einem solchen Verhalten im Allgemeinen umgeht. jhale würde wahrscheinlich lieber nicht seinen Programmcode ändern, sondern nur seine Serverkonfiguration.
Lutz Prechelt
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.