Perbandingan string tidak peka huruf besar / kecil di LINQ-to-SQL


137

Saya telah membaca bahwa tidak bijaksana menggunakan ToUpper dan ToLower untuk melakukan perbandingan string yang tidak peka huruf besar / kecil, tetapi saya tidak melihat alternatif lain dalam hal LINQ-to-SQL. Argumen ignoreCase dan CompareOptions dari String.Compare diabaikan oleh LINQ-to-SQL (jika Anda menggunakan database case-sensitive, Anda mendapatkan perbandingan case-sensitive bahkan jika Anda meminta perbandingan case-sensitive). Apakah ToLower atau ToUpper merupakan opsi terbaik di sini? Apakah yang satu lebih baik dari yang lain? Saya pikir saya pernah membaca bahwa ToUpper lebih baik, tetapi saya tidak tahu apakah itu berlaku di sini. (Saya melakukan banyak tinjauan kode dan semua orang menggunakan ToLower.)

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

Ini diterjemahkan menjadi kueri SQL yang hanya membandingkan row.Name dengan "test" dan tidak akan mengembalikan "Test" dan "TEST" pada database case-sensitive.


1
Terima kasih! Ini benar-benar menyelamatkan pantatku hari ini. Catatan: ini bekerja dengan ekstensi LINQ lain juga seperti LINQQuery.Contains("VaLuE", StringComparer.CurrentCultureIgnoreCase)dan LINQQuery.Except(new string[]{"A VaLUE","AnOTher VaLUE"}, StringComparer.CurrentCultureIgnoreCase). Wahoo!
Greg Bray

Lucu, saya baru saja membaca bahwa ToUpper lebih baik dalam perbandingan dari sumber ini: msdn.microsoft.com/en-us/library/dd465121
malckier

Jawaban:


111

Seperti yang Anda katakan, ada beberapa perbedaan penting antara ToUpper dan ToLower, dan hanya satu yang dapat diandalkan akurat saat Anda mencoba melakukan pemeriksaan persamaan tidak peka huruf besar / kecil.

Idealnya, cara terbaik untuk melakukan pemeriksaan persamaan tidak peka huruf besar / kecil adalah :

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

CATATAN, NAMUN bahwa ini tidak berfungsi dalam kasus ini! Oleh karena itu kita terjebak dengan ToUpperatau ToLower.

Perhatikan Ordinal IgnoreCase untuk membuatnya aman bagi keamanan. Tapi sebenarnya jenis pemeriksaan sensitif huruf besar / kecil yang Anda gunakan bergantung pada tujuan Anda. Namun secara umum, gunakan Sama untuk pemeriksaan kesetaraan dan Bandingkan saat Anda menyortir, lalu pilih Perbandingan String yang tepat untuk pekerjaan itu.

Michael Kaplan (otoritas yang diakui pada budaya dan penanganan karakter seperti ini) memiliki posting yang relevan di ToUpper vs. ToLower:

Dia mengatakan "String.ToUpper - Gunakan ToUpper daripada ToLower, dan tentukan InvariantCulture untuk mengambil aturan casing OS "


1
Tampaknya ini tidak berlaku untuk SQL Server: print upper ('Große Straße') mengembalikan GROßE STRAßE
BlueMonkMN

1
Selain itu, kode contoh yang Anda berikan memiliki masalah yang sama dengan kode yang saya berikan sejauh peka huruf besar / kecil saat dijalankan melalui LINQ-to-SQL pada database MS SQL 2005.
BlueMonkMN

2
Saya setuju. Maaf saya tidak jelas. Kode contoh yang saya berikan tidak berfungsi dengan Linq2Sql seperti yang Anda tunjukkan di pertanyaan awal Anda. Saya hanya menyatakan kembali bahwa cara Anda memulai adalah cara yang bagus untuk pergi - jika hanya berhasil dalam skenario ini. Dan ya, kotak sabun Mike Kaplan lainnya adalah bahwa penanganan karakter SQL Server ada di mana-mana. Jika Anda memerlukan case insensitive dan tidak bisa mendapatkannya dengan cara lain, saya menyarankan (tidak jelas) agar Anda menyimpan data sebagai huruf besar, dan kemudian menanyakannya sebagai huruf besar.
Andrew Arnott

3
Nah, jika Anda memiliki database case-sensitive, dan Anda menyimpan dalam huruf besar-kecil dan mencari dalam huruf kapital, Anda tidak akan mendapatkan kecocokan. Jika Anda meningkatkan data dan kueri dalam penelusuran Anda, maka Anda mengonversi semua teks yang Anda telusuri untuk setiap kueri, yang tidak berkinerja baik.
Andrew Arnott

1
@BlueMonkMN, apakah Anda yakin telah menempelkan cuplikan yang benar? Sulit dipercaya bahwa Server MSSQL lebih memilih Merah daripada Hitam.
greenoldman

74

Saya digunakan System.Data.Linq.SqlClient.SqlMethods.Like(row.Name, "test") dalam permintaan saya.

Ini melakukan perbandingan tidak peka huruf besar / kecil.


3
Ha! telah menggunakan linq 2 sql selama beberapa tahun sekarang tetapi belum melihat SqlMethods sampai sekarang, terima kasih!
Carl Hörberg

3
Cemerlang! Bisa menggunakan lebih banyak detail. Apakah ini salah satu kegunaan Like yang diharapkan? Apakah ada masukan yang mungkin menyebabkan hasil positif palsu? Atau hasil negatif palsu? Dokumentasi tentang metode ini kurang, di manakah dokumentasi yang akan menjelaskan pengoperasian metode Like?
Tugas

2
Saya pikir itu hanya bergantung pada bagaimana SQL Server membandingkan string, yang mungkin dapat dikonfigurasi di suatu tempat.
Andrew Davey

12
System.Data.Linq.SqlClient.SqlMethods.Like (row.Name, "test") sama dengan row.Name.Contains ("test"). Seperti yang dikatakan Andrew, ini tergantung pada pemeriksaan sql server. Jadi Suka (atau berisi) tidak selalu melakukan perbandingan tidak peka huruf besar / kecil.
doekman

3
Sadarilah, ini membuat kode terlalu berpasangan SqlClient.
Jaider

4

Saya mencoba ini menggunakan ekspresi Lambda, dan berhasil.

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


19
Itu karena Anda menggunakan a List<>, yang berarti perbandingan terjadi di memori (kode C #) daripada fileIQueryable (atau ObjectQuery) yang akan melakukan perbandingan di database .
drzaus

1
Apa yang dikatakan @dr. Jawaban ini salah, mengingat konteksnya adalah linq2sql, dan bukan linq biasa.
rsenna

0

Jika Anda meneruskan string yang case-insensitive ke LINQ-to-SQL, string tersebut akan diteruskan ke SQL tanpa perubahan dan perbandingan akan terjadi di database. Jika Anda ingin melakukan perbandingan string tidak peka huruf besar / kecil dalam database, yang perlu Anda lakukan hanyalah membuat ekspresi lambda yang melakukan perbandingan dan penyedia LINQ-to-SQL akan menerjemahkan ekspresi itu ke dalam kueri SQL dengan string Anda utuh.

Misalnya kueri LINQ ini:

from user in Users
where user.Email == "foo@bar.com"
select user

diterjemahkan ke SQL berikut oleh penyedia LINQ-to-SQL:

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 "foo@bar.com"

Seperti yang Anda lihat, parameter string akan dibandingkan dalam SQL yang berarti segala sesuatunya harus berfungsi seperti yang Anda harapkan.


Saya tidak mengerti apa yang Anda katakan. 1) String itu sendiri tidak boleh case-insensitive atau case-sensitive di .NET, jadi saya tidak bisa memberikan "string case-sensitive". 2) Query LINQ pada dasarnya ADALAH ekspresi lambda, dan begitulah cara saya meneruskan dua string saya, jadi ini tidak masuk akal bagi saya.
BlueMonkMN

3
Saya ingin melakukan perbandingan CASE-INSENSITIVE pada database SENSITIF KASUS.
BlueMonkMN

Database CASE-SENSITIVE apa yang Anda gunakan?
Andrew Hare

Selain itu, kueri LINQ bukanlah ekspresi lambda. Kueri LINQ terdiri dari beberapa bagian (terutama operator kueri dan ekspresi lambda).
Andrew Hare

Jawaban ini tidak masuk akal karena komentar BlueMonkMN.
Alf

0

Untuk melakukan query Linq ke Sql case sensitive mendeklarasikan field 'string' menjadi case sensitive dengan menentukan tipe data server dengan menggunakan salah satu dari berikut ini;

varchar(4000) COLLATE SQL_Latin1_General_CP1_CS_AS 

atau

nvarchar(Max) COLLATE SQL_Latin1_General_CP1_CS_AS

Catatan: 'CS' pada jenis pemeriksaan di atas berarti 'Peka Huruf Besar-Kecil'.

Ini dapat dimasukkan di bidang "Jenis Data Server" saat melihat properti menggunakan Visual Studio DBML Designer.

Untuk lebih jelasnya lihat http://yourdotnetdesignteam.blogspot.com/2010/06/case-sensitive-linq-to-sql-queries.html


Itulah masalahnya. Biasanya bidang yang saya gunakan peka huruf besar / kecil (rumus kimia CO [karbon monoksida] berbeda dari Co [kobalt]). Namun, dalam situasi tertentu (pencarian) saya ingin co mencocokkan Co dan CO. Mendefinisikan properti tambahan dengan "tipe data server" yang berbeda tidak legal (LINQ ke Sql hanya mengizinkan satu properti per kolom sql). Jadi tetap tidak pergi.
doekman

Selain itu, jika melakukan Pengujian Unit, pendekatan ini kemungkinan tidak akan kompatibel dengan tiruan data. Paling baik menggunakan pendekatan linq / lambda dalam jawaban yang diterima.
Derrick

0
where row.name.StartsWith(q, true, System.Globalization.CultureInfo.CurrentCulture)

1
Apa teks SQL yang menerjemahkannya, dan apa yang memungkinkannya menjadi case insensitive dalam lingkungan SQL yang sebaliknya akan memperlakukannya sebagai case-sensitive?
BlueMonkMN

0

Pendekatan 2-tahap berikut berfungsi untuk saya (VS2010, ASP.NET MVC3, SQL Server 2008, Linq ke SQL):

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

if (caseSensitive)
{
    result = result
        .Where(e => e.EntitySearchText.IndexOf(item, System.StringComparison.CurrentCulture) >= 0);
}

1
Kode ini memiliki bug jika teks dimulai dengan teks pencarian (harus> = 0)
Flatliner DOA

@FlatlinerDOA seharusnya != -1karena IndexOf "mengembalikan -1 jika karakter atau string tidak ditemukan"
drzaus

0

Terkadang nilai yang disimpan dalam Database bisa berisi spasi sehingga menjalankan ini bisa gagal

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

Solusi untuk masalah ini adalah dengan menghapus spasi kemudian mengubah case-nya kemudian pilih seperti ini

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

Perhatikan dalam kasus ini

customname adalah nilai yang sesuai dengan nilai Database

UsersTBs adalah kelas

judul adalah kolom Database


-1

Ingatlah bahwa ada perbedaan antara apakah kueri berfungsi dan apakah berfungsi secara efisien ! Pernyataan LINQ akan diubah menjadi T-SQL jika target pernyataan tersebut adalah SQL Server, jadi Anda perlu memikirkan tentang T-SQL yang akan diproduksi.

Menggunakan String.Equals kemungkinan besar (saya menebak) akan membawa kembali semua baris dari SQL Server dan kemudian melakukan perbandingan dalam .NET, karena ini adalah ekspresi .NET yang tidak dapat diterjemahkan ke dalam T-SQL.

Dengan kata lain, menggunakan ekspresi akan meningkatkan akses data Anda dan menghilangkan kemampuan Anda untuk menggunakan indeks. Ini akan berfungsi pada tabel kecil dan Anda tidak akan melihat perbedaannya. Di atas meja besar itu bisa berkinerja sangat buruk.

Itulah salah satu masalah yang ada dengan LINQ; orang tidak lagi memikirkan bagaimana pernyataan yang mereka tulis akan dipenuhi.

Dalam kasus ini tidak ada cara untuk melakukan apa yang Anda inginkan tanpa menggunakan ekspresi - bahkan di T-SQL. Oleh karena itu, Anda mungkin tidak dapat melakukan ini dengan lebih efisien. Bahkan jawaban T-SQL yang diberikan di atas (menggunakan variabel dengan pemeriksaan) kemungkinan besar akan mengakibatkan indeks diabaikan, tetapi jika itu adalah tabel besar maka ada baiknya menjalankan pernyataan dan melihat rencana eksekusi untuk melihat apakah indeks digunakan .


2
Itu tidak benar (itu tidak menyebabkan baris dikembalikan ke klien). Saya telah menggunakan String.Equals dan alasannya tidak berfungsi adalah karena diubah menjadi perbandingan string TSQL, yang perilakunya bergantung pada pemeriksaan database atau server. Saya untuk satu mempertimbangkan bagaimana setiap LINQ ke ekspresi SQL yang saya tulis akan diubah menjadi TSQL. Cara untuk mencapai apa yang saya inginkan adalah menggunakan ToUpper untuk memaksa TSQL yang dihasilkan menggunakan UPPER. Maka semua logika konversi dan perbandingan masih dilakukan di TSQL sehingga Anda tidak kehilangan banyak performa.
BlueMonkMN
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.