Vergleich von Zeichenfolgen ohne Berücksichtigung der Groß- und Kleinschreibung in LINQ-to-SQL

137

Ich habe gelesen, dass es unklug ist, ToUpper und ToLower zu verwenden, um Zeichenfolgenvergleiche ohne Berücksichtigung der Groß- und Kleinschreibung durchzuführen, aber ich sehe keine Alternative, wenn es um LINQ-to-SQL geht. Die Argumente ignoreCase und CompareOptions von String.Compare werden von LINQ-to-SQL ignoriert (wenn Sie eine Datenbank verwenden, bei der zwischen Groß- und Kleinschreibung unterschieden wird, erhalten Sie einen Vergleich zwischen Groß- und Kleinschreibung, auch wenn Sie nach einem Vergleich ohne Berücksichtigung von Groß- und Kleinschreibung fragen). Ist ToLower oder ToUpper hier die beste Option? Ist einer besser als der andere? Ich dachte, ich hätte irgendwo gelesen, dass ToUpper besser ist, aber ich weiß nicht, ob das hier zutrifft. (Ich mache viele Codeüberprüfungen und jeder verwendet ToLower.)

Dim s = From row In context.Table Where String.Compare(row.Name, "test", StringComparison.InvariantCultureIgnoreCase) = 0

Dies führt zu einer SQL-Abfrage, die row.Name einfach mit "test" vergleicht und in einer Datenbank mit Groß- und Kleinschreibung nicht "Test" und "TEST" zurückgibt.

BlueMonkMN
quelle
1
Vielen Dank! Das hat mir heute wirklich den Arsch gerettet. Hinweis: Es funktioniert auch mit anderen LINQ-Erweiterungen wie LINQQuery.Contains("VaLuE", StringComparer.CurrentCultureIgnoreCase)und LINQQuery.Except(new string[]{"A VaLUE","AnOTher VaLUE"}, StringComparer.CurrentCultureIgnoreCase). Wahoo!
Greg Bray
Komisch, ich hatte gerade gelesen, dass ToUpper in Vergleichen aus dieser Quelle besser war: msdn.microsoft.com/en-us/library/dd465121
malckier

Antworten:

110

Wie Sie sagen, gibt es einige wichtige Unterschiede zwischen ToUpper und ToLower, und nur einer ist zuverlässig genau, wenn Sie versuchen, Gleichheitsprüfungen ohne Berücksichtigung der Groß- und Kleinschreibung durchzuführen.

Im Idealfall ist der beste Weg, eine Gleichstellungsprüfung ohne Berücksichtigung der Groß- und Kleinschreibung durchzuführen, folgende :

String.Equals(row.Name, "test", StringComparison.OrdinalIgnoreCase)

HINWEIS, dass dies in diesem Fall jedoch nicht funktioniert ! Deshalb stecken wir mit ToUpperoder fest ToLower.

Beachten Sie den Ordinal IgnoreCase, um die Sicherheit zu gewährleisten. Die genaue Art der (in) sensiblen Prüfung, die Sie verwenden, hängt jedoch von Ihren Zwecken ab. Verwenden Sie im Allgemeinen "Gleich" für Gleichheitsprüfungen und "Vergleichen", wenn Sie sortieren, und wählen Sie dann den richtigen StringComparison für den Job aus.

Michael Kaplan (eine anerkannte Autorität für Kultur und Umgang mit Charakteren wie diese) hat relevante Beiträge zu ToUpper vs. ToLower:

Er sagt "String.ToUpper - Verwenden Sie ToUpper anstelle von ToLower und geben Sie InvariantCulture an, um die OS-Gehäuseregeln zu übernehmen. "

Andrew Arnott
quelle
1
Dies gilt anscheinend nicht für SQL Server: Print Upper ('Große Straße') gibt GROßE STRAßE
BlueMonkMN
1
Außerdem hat der von Ihnen bereitgestellte Beispielcode das gleiche Problem wie der von mir bereitgestellte Code, da bei der Ausführung über LINQ-to-SQL in einer MS SQL 2005-Datenbank zwischen Groß- und Kleinschreibung unterschieden wird.
BlueMonkMN
2
Genau. Entschuldigung, ich war unklar. Der von mir bereitgestellte Beispielcode funktioniert nicht mit Linq2Sql, wie Sie in Ihrer ursprünglichen Frage ausgeführt haben. Ich habe nur wiederholt, dass der Weg, den Sie begonnen haben, ein guter Weg war - wenn es nur in diesem Szenario funktioniert hätte. Und ja, eine andere Seifenkiste von Mike Kaplan ist, dass die Zeichenbehandlung von SQL Server allgegenwärtig ist. Wenn Sie die Groß- und Kleinschreibung nicht berücksichtigen müssen und es nicht anders erhalten können, habe ich (unklar) vorgeschlagen, die Daten als Großbuchstaben zu speichern und sie dann als Großbuchstaben abzufragen.
Andrew Arnott
3
Wenn Sie eine Datenbank mit Groß- und Kleinschreibung haben und in Groß- und Kleinschreibung speichern und in Großbuchstaben suchen, erhalten Sie keine Übereinstimmungen. Wenn Sie sowohl die Daten als auch die Abfrage in Ihrer Suche hochschreiben, konvertieren Sie den gesamten Text, nach dem Sie suchen, für jede Abfrage, die nicht performant ist.
Andrew Arnott
1
@BlueMonkMN, sind Sie sicher, dass Sie die richtigen Schnipsel eingefügt haben? Es ist kaum zu glauben, dass MSSQL Server Rot mehr als Schwarz bevorzugt.
Greenoldman
75

Ich habe System.Data.Linq.SqlClient.SqlMethods.Like(row.Name, "test") in meiner Abfrage verwendet.

Dies führt einen Vergleich ohne Berücksichtigung der Groß- und Kleinschreibung durch.

Andrew Davey
quelle
3
Ha! Ich benutze Linq 2 SQL seit einigen Jahren, habe aber SqlMethods bis jetzt noch nicht gesehen, danke!
Carl Hörberg
3
Brillant! Könnte aber mehr Details gebrauchen. Ist dies eine der erwarteten Verwendungen von Like? Gibt es mögliche Eingaben, die zu einem falsch positiven Ergebnis führen würden? Oder ein falsch negatives Ergebnis? Die Dokumentation zu dieser Methode fehlt, wo ist die Dokumentation wird den Betrieb der Wie Methode beschreiben?
Aufgabe
2
Ich denke, es hängt nur davon ab, wie SQL Server die Zeichenfolgen vergleicht, was wahrscheinlich irgendwo konfigurierbar ist.
Andrew Davey
11
System.Data.Linq.SqlClient.SqlMethods.Like (row.Name, "test") ist dasselbe wie row.Name.Contains ("test"). Wie Andrew sagt, hängt dies von der Sortierung des SQL-Servers ab. Like (oder enthält) führt also nicht immer einen Vergleich ohne Berücksichtigung der Groß- und Kleinschreibung durch.
Doekman
3
Beachten Sie, dass dies den Code zu koppelt SqlClient.
Jaider
5

Ich habe es mit dem Lambda-Ausdruck versucht und es hat funktioniert.

List<MyList>.Any (x => (String.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)) && (x.Type == qbType) );

vinahr
quelle
18
Dies liegt daran, dass Sie a verwenden. Dies List<>bedeutet, dass der Vergleich im Arbeitsspeicher (C # -Code) statt eines IQueryable(oder ObjectQuery) erfolgt, der den Vergleich in der Datenbank durchführen würde .
Drzaus
1
Was @drzaus gesagt hat. Diese Antwort ist einfach falsch, wenn man bedenkt, dass der Kontext linq2sql und nicht reguläres linq ist.
Rsenna
0

Wenn Sie eine Zeichenfolge, bei der die Groß- und Kleinschreibung nicht berücksichtigt wird, an LINQ-to-SQL übergeben, wird diese unverändert an SQL übergeben, und der Vergleich wird in der Datenbank durchgeführt. Wenn Sie Zeichenfolgenvergleiche in der Datenbank ohne Berücksichtigung der Groß- und Kleinschreibung durchführen möchten, müssen Sie lediglich einen Lambda-Ausdruck erstellen, der den Vergleich durchführt, und der LINQ-zu-SQL-Anbieter übersetzt diesen Ausdruck in eine SQL-Abfrage mit intakter Zeichenfolge.

Zum Beispiel diese LINQ-Abfrage:

from user in Users
where user.Email == "[email protected]"
select user

wird vom LINQ-to-SQL-Anbieter in das folgende SQL übersetzt:

SELECT [t0].[Email]
FROM [User] AS [t0]
WHERE [t0].[Email] = @p0
-- note that "@p0" is defined as nvarchar(11)
-- and is passed my value of "[email protected]"

Wie Sie sehen können, wird der Zeichenfolgenparameter in SQL verglichen, was bedeutet, dass die Dinge genau so funktionieren sollten, wie Sie es erwarten würden.

Andrew Hare
quelle
Ich verstehe nicht, was du sagst. 1) Bei Zeichenfolgen selbst kann in .NET nicht zwischen Groß- und Kleinschreibung unterschieden werden, daher kann ich keine Zeichenfolge ohne Berücksichtigung der Groß- und Kleinschreibung übergeben. 2) Eine LINQ-Abfrage ist im Grunde genommen ein Lambda-Ausdruck, und so übergebe ich meine beiden Zeichenfolgen, sodass dies für mich keinen Sinn ergibt.
BlueMonkMN
3
Ich möchte einen CASE-INSENSITIVE-Vergleich für eine CASE-SENSITIVE-Datenbank durchführen.
BlueMonkMN
Welche CASE-SENSITIVE-Datenbank verwenden Sie?
Andrew Hare
Außerdem ist eine LINQ-Abfrage kein Lambda-Ausdruck. Eine LINQ-Abfrage besteht aus mehreren Teilen (insbesondere Abfrageoperatoren und Lambda-Ausdrücke).
Andrew Hare
Diese Antwort ist als BlueMonkMN-Kommentar nicht sinnvoll.
Alf
0

Um zwischen Groß- und Kleinschreibung unterscheidende Linq-zu-SQL-Abfragen durchzuführen, deklarieren Sie 'String'-Felder als Groß- und Kleinschreibung, indem Sie den Server-Datentyp mithilfe einer der folgenden Optionen angeben.

varchar(4000) COLLATE SQL_Latin1_General_CP1_CS_AS 

oder

nvarchar(Max) COLLATE SQL_Latin1_General_CP1_CS_AS

Hinweis: Das 'CS' in den obigen Sortierungstypen bedeutet 'Groß- und Kleinschreibung beachten'.

Dies kann in das Feld "Serverdatentyp" eingegeben werden, wenn eine Eigenschaft mit Visual Studio DBML Designer angezeigt wird.

Weitere Informationen finden Sie unter http://yourdotnetdesignteam.blogspot.com/2010/06/case-sensitive-linq-to-sql-queries.html

John Hansen
quelle
Das ist das Problem. Normalerweise unterscheidet das von mir verwendete Feld zwischen Groß- und Kleinschreibung (die chemische Formel CO [Kohlenmonoxid] unterscheidet sich von Co [Kobalt]). In einer bestimmten Situation (Suche) möchte ich jedoch, dass Co sowohl mit Co als auch mit CO übereinstimmt. Das Definieren einer zusätzlichen Eigenschaft mit einem anderen "Server-Datentyp" ist nicht zulässig (linq to sql erlaubt nur eine Eigenschaft pro SQL-Spalte). Also immer noch nicht los.
Doekman
Wenn Sie Unit-Tests durchführen, ist dieser Ansatz wahrscheinlich nicht mit einem Datenmodell kompatibel. Verwenden Sie am besten den Linq / Lambda-Ansatz in der akzeptierten Antwort.
Derrick
0
where row.name.StartsWith(q, true, System.Globalization.CultureInfo.CurrentCulture)
Julio Silveira
quelle
1
Was ist der SQL-Text, in den dies übersetzt wird, und was ermöglicht es, in einer SQL-Umgebung, in der die Groß- und Kleinschreibung nicht berücksichtigt wird, zwischen Groß- und Kleinschreibung zu unterscheiden?
BlueMonkMN
0

Der folgende zweistufige Ansatz funktioniert für mich (VS2010, ASP.NET MVC3, SQL Server 2008, Linq to SQL):

result = entRepos.FindAllEntities()
    .Where(e => e.EntitySearchText.Contains(item));

if (caseSensitive)
{
    result = result
        .Where(e => e.EntitySearchText.IndexOf(item, System.StringComparison.CurrentCulture) >= 0);
}
Jim Davies
quelle
1
Dieser Code hat einen Fehler, wenn der Text mit dem Suchtext beginnt (sollte> = 0 sein)
Flatliner DOA
@FlatlinerDOA sollte es eigentlich sein, != -1weil IndexOf "-1 zurückgibt, wenn das Zeichen oder die Zeichenfolge nicht gefunden wird"
drzaus
0

Manchmal kann der in der Datenbank gespeicherte Wert Leerzeichen enthalten, sodass die Ausführung fehlschlagen kann

String.Equals(row.Name, "test", StringComparison.OrdinalIgnoreCase)

Die Lösung für dieses Problem besteht darin, Leerzeichen zu entfernen, das Gehäuse zu konvertieren und dann wie folgt auszuwählen

 return db.UsersTBs.Where(x => x.title.ToString().ToLower().Replace(" ",string.Empty).Equals(customname.ToLower())).FirstOrDefault();

Beachten Sie in diesem Fall

Benutzerdefinierter Name ist ein Wert, der mit dem Datenbankwert übereinstimmt

UsersTBs ist eine Klasse

Titel ist die Spalte Datenbank

TAHA SULTAN TEMURI
quelle
-1

Denken Sie daran, dass es einen Unterschied gibt, ob die Abfrage funktioniert und ob sie effizient funktioniert ! Eine LINQ-Anweisung wird in T-SQL konvertiert, wenn das Ziel der Anweisung SQL Server ist. Sie müssen also über das erzeugte T-SQL nachdenken.

Die Verwendung von String.Equals bringt höchstwahrscheinlich (ich vermute) alle Zeilen von SQL Server zurück und führt dann den Vergleich in .NET durch, da es sich um einen .NET-Ausdruck handelt, der nicht in T-SQL übersetzt werden kann.

Mit anderen Worten, die Verwendung eines Ausdrucks erhöht Ihren Datenzugriff und verhindert, dass Sie Indizes verwenden können. Es funktioniert auf kleinen Tischen und Sie werden den Unterschied nicht bemerken. Auf einem großen Tisch könnte es sehr schlecht abschneiden.

Dies ist eines der Probleme, die bei LINQ bestehen. Menschen denken nicht mehr darüber nach, wie die Aussagen, die sie schreiben, erfüllt werden.

In diesem Fall gibt es keine Möglichkeit, ohne Verwendung eines Ausdrucks das zu tun, was Sie möchten - auch nicht in T-SQL. Daher können Sie dies möglicherweise nicht effizienter durchführen. Selbst die oben angegebene T-SQL-Antwort (unter Verwendung von Variablen mit Sortierung) führt höchstwahrscheinlich dazu, dass Indizes ignoriert werden. Wenn es sich jedoch um eine große Tabelle handelt, lohnt es sich, die Anweisung auszuführen und den Ausführungsplan zu überprüfen, um festzustellen, ob ein Index verwendet wurde .

Andrew H.
quelle
2
Das stimmt nicht (es führt nicht dazu, dass die Zeilen an den Client zurückgegeben werden). Ich habe String.Equals verwendet und der Grund, warum es nicht funktioniert, ist, dass es in einen TSQL-String-Vergleich konvertiert wird, dessen Verhalten von der Sortierung der Datenbank oder des Servers abhängt. Ich für meinen Teil überlege, wie jeder LINQ to SQL-Ausdruck, den ich schreibe, in TSQL konvertiert wird. Der Weg zu dem, was ich möchte, besteht darin, ToUpper zu verwenden, um die generierte TSQL zu zwingen, UPPER zu verwenden. Dann wird die gesamte Konvertierungs- und Vergleichslogik immer noch in TSQL ausgeführt, sodass Sie nicht viel Leistung verlieren.
BlueMonkMN