Kasus mengabaikan SQL server dalam ekspresi where

91

Bagaimana cara membuat kueri SQL (MS SQL Server) di mana klausa "di mana" tidak peka huruf besar / kecil?

SELECT * FROM myTable WHERE myField = 'sOmeVal'

Saya ingin hasilnya kembali dengan mengabaikan kasus ini

Raul Agrait
sumber

Jawaban:

137

Dalam konfigurasi default database SQL Server, perbandingan string adalah case-sensitive. Jika database Anda menimpa pengaturan ini (melalui penggunaan pemeriksaan alternatif), maka Anda harus menentukan jenis pemeriksaan yang akan digunakan dalam kueri Anda.

SELECT * FROM myTable WHERE myField = 'sOmeVal' COLLATE SQL_Latin1_General_CP1_CI_AS

Perhatikan bahwa pemeriksaan yang saya berikan hanyalah sebuah contoh (meskipun kemungkinan besar akan berfungsi dengan baik untuk Anda). Garis besar pemeriksaan SQL Server yang lebih menyeluruh dapat ditemukan di sini .

Adam Robinson
sumber
Sekadar konfirmasi, ini hanya perlu ditambahkan satu kali, di akhir WHEREpernyataan, dan akan memengaruhi semua WHEREklausa, bukan?
ashleedawg
Ingin tahu apakah jawaban Anda memiliki masalah kinerja dengan mengonversi nilai kolom menjadi UPPERatau LOWERkapitalisasi lalu menggunakan LIKEuntuk mencari?
Shaiju T
1
@ashleedawg - pertanyaan bagus .. tampaknya pengaturan per baris.
Leo Gurdian
30

Biasanya, perbandingan string tidak peka huruf besar / kecil. Jika database Anda dikonfigurasi untuk pemeriksaan case-sensitive, Anda perlu memaksa untuk menggunakan case tidak sensitif:

SELECT balance FROM people WHERE email = '[email protected]'
  COLLATE SQL_Latin1_General_CP1_CI_AS 
Andrejs Cainikovs
sumber
@Bayu_joo dan Andrejs: Secara teknis, ini bukanlah masalah konfigurasi database. Silakan lihat jawaban saya untuk klarifikasi tentang perbandingan string.
Solomon Rutzky
21

Saya menemukan solusi lain di tempat lain; yaitu, untuk digunakan

upper(@yourString)

tetapi semua orang di sini mengatakan bahwa, di SQL Server, tidak masalah karena tetap mengabaikan kasus? Saya cukup yakin database kita peka huruf besar kecil.

Danny
sumber
7
Anda benar bahwa database dapat dibuat peka huruf besar / kecil, tetapi ini sangat tidak efisien, bahkan jika diperlukan. COLLATE adalah kata kunci yang akan digunakan.
mjaggard
1
Terima kasih telah mengungkitnya, @mjaggard. Saya harap Anda, atau siapa pun yang tampaknya tidak menyukai jawaban saya, menjelaskan demi kebaikan siapa pun seperti saya yang mencari dan menemukan jawaban seperti saya.
Danny
1
Suara positif ini karena ini adalah penjelasan yang sangat rasional. Susun terlalu banyak overhead dan bagaimana jika string Anda memiliki karakter di dalamnya yang tidak dipahami oleh pemeriksaan? Latin 1 adalah skema pengkodean yang buruk. Semoga berhasil mendapatkan hasil yang berarti jika string Anda memiliki apostrof di dalamnya (Seperti: O'Brien).
eggmatters
2
Suara positif juga. Saya dapat memikirkan banyak kasus di mana ini akan berguna. Selain itu, sering kali ada lebih dari satu cara yang baik untuk melakukan sesuatu.
Inversus
1
Mengubah kasus string untuk tujuan perbandingan umumnya buruk. Dalam beberapa kasus bahasa, konversi tidak bolak-balik. yaitu LOWER (x)! = LOWER (UPPER (x)).
Ceisc
17

2 jawaban teratas (dari Adam Robinson dan Andrejs Cainikovs ) agak, agak benar, karena secara teknis berhasil, tetapi penjelasan mereka salah dan bisa menyesatkan dalam banyak kasus. Misalnya, meskipun SQL_Latin1_General_CP1_CI_ASpemeriksaan akan berfungsi dalam banyak kasus, pemeriksaan tersebut tidak boleh dianggap sebagai pemeriksaan tidak peka huruf besar / kecil. Faktanya, mengingat bahwa OP bekerja dalam database dengan pemeriksaan case-sensitive (atau mungkin biner), kita tahu bahwa OP tidak menggunakan pemeriksaan yang merupakan default untuk begitu banyak instalasi (terutama yang diinstal pada OS. menggunakan bahasa Inggris AS sebagai bahasa): SQL_Latin1_General_CP1_CI_AS. Tentu, OP bisa digunakan SQL_Latin1_General_CP1_CS_AS, tapi saat bekerja denganVARCHARdata, penting untuk tidak mengubah halaman kode karena dapat menyebabkan hilangnya data, dan itu dikontrol oleh lokal / budaya pemeriksaan (yaitu Latin1_General vs Perancis vs Ibrani dll). Silakan lihat poin # 9 di bawah.

Empat jawaban lainnya salah dalam berbagai tingkatan.

Saya akan mengklarifikasi semua kesalahpahaman di sini sehingga pembaca dapat membuat pilihan yang paling tepat / efisien.

  1. Jangan gunakan UPPER(). Itu benar-benar pekerjaan ekstra yang tidak perlu. Gunakan COLLATEklausa. Perbandingan string perlu dilakukan dalam kedua kasus, tetapi menggunakan UPPER()juga harus memeriksa, karakter demi karakter, untuk melihat apakah ada pemetaan huruf besar, dan kemudian mengubahnya. Dan Anda perlu melakukan ini di kedua sisi. Menambahkan COLLATEhanya mengarahkan pemrosesan untuk menghasilkan kunci sortir menggunakan sekumpulan aturan yang berbeda dari yang akan dilakukan secara default. Menggunakan COLLATEpasti lebih efisien (atau "performant", jika Anda suka kata itu :) daripada menggunakan UPPER(), sebagaimana dibuktikan dalam skrip pengujian ini (di PasteBin) .

    Ada juga masalah yang dicatat oleh @Ceisc pada jawaban @ Danny:

    Dalam beberapa kasus bahasa, konversi tidak bolak-balik. yaitu LOWER (x)! = LOWER (UPPER (x)).

    Huruf besar Turki "İ" adalah contoh yang umum.

  2. Tidak, pemeriksaan bukanlah pengaturan seluruh database, setidaknya tidak dalam konteks ini. Ada pemeriksaan default tingkat database, dan digunakan sebagai default untuk kolom yang diubah dan yang baru dibuat yang tidak menentukan COLLATEklausa (yang mungkin berasal dari kesalahpahaman umum ini), tetapi tidak memengaruhi kueri secara langsung kecuali Anda adalah membandingkan literal string dan variabel dengan literal dan variabel string lain, atau Anda mereferensikan meta-data tingkat database.

  3. Tidak, pemeriksaan tidak dilakukan per kueri.

  4. Collations adalah per predikat (yaitu sesuatu operan sesuatu) atau ekspresi, bukan per query. Dan ini berlaku untuk seluruh kueri, bukan hanya WHEREklausa. Ini mencakup JOIN, GROUP BY, ORDER BY, PARTITION BY, dll.

  5. Tidak, jangan ubah ke VARBINARY(mis. convert(varbinary, myField) = convert(varbinary, 'sOmeVal')) Karena alasan berikut:

    1. itu adalah perbandingan biner, yang tidak case-insensitive (yang ditanyakan oleh pertanyaan ini)
    2. jika Anda memang menginginkan perbandingan biner, gunakan pemeriksaan biner. Gunakan yang diakhiri dengan _BIN2jika Anda menggunakan SQL Server 2008 atau yang lebih baru, jika tidak, Anda tidak punya pilihan selain menggunakan yang diakhiri dengan _BIN. Jika datanya NVARCHARmaka tidak masalah lokal mana yang Anda gunakan karena semuanya sama dalam kasus itu, karenanya Latin1_General_100_BIN2selalu berfungsi. Jika data VARCHAR, Anda harus menggunakan lokal yang sama bahwa data saat ini dalam (misalnya Latin1_General, French, Japanese_XJIS, dll) karena lokal menentukan halaman kode yang digunakan, dan mengubah halaman kode dapat mengubah data (yaitu kehilangan data).
    3. menggunakan tipe data variabel-panjang tanpa menentukan ukuran akan bergantung pada ukuran default, dan ada dua default yang berbeda tergantung pada konteks di mana tipe data tersebut digunakan. Bisa 1 atau 30 untuk tipe string. Saat digunakan dengan CONVERT()itu akan menggunakan 30 nilai default. Bahayanya adalah, jika string bisa lebih dari 30 byte, itu akan terpotong secara diam-diam dan Anda kemungkinan akan mendapatkan hasil yang salah dari predikat ini.
    4. Meskipun Anda menginginkan perbandingan yang peka huruf besar kecil, pemeriksaan biner tidak peka huruf besar / kecil (kesalahpahaman lain yang sangat umum).
  6. Tidak, LIKEtidak selalu peka huruf besar kecil. Ini menggunakan pemeriksaan kolom yang direferensikan, atau pemeriksaan database jika variabel dibandingkan dengan string literal, atau pemeriksaan ditentukan melalui COLLATEklausa opsional .

  7. LCASEbukanlah fungsi SQL Server. Tampaknya itu adalah Oracle atau MySQL. Atau mungkin Visual Basic?

  8. Karena konteks pertanyaannya adalah membandingkan kolom dengan string literal, baik pemeriksaan instance (sering disebut sebagai "server") maupun pemeriksaan database tidak berdampak langsung di sini. Kumpulan disimpan per setiap kolom, dan setiap kolom dapat memiliki pemeriksaan yang berbeda, dan pemeriksaan tersebut tidak perlu sama dengan pemeriksaan default database atau pemeriksaan instance. Tentu, pemeriksaan instance adalah default untuk apa yang akan digunakan database yang baru dibuat sebagai pemeriksaan default jika COLLATEklausa tidak ditentukan saat membuat database. Dan juga, pemeriksaan default database adalah apa yang akan digunakan kolom yang diubah atau yang baru dibuat jika COLLATEklausa tidak ditentukan.

  9. Anda harus menggunakan pemeriksaan case-insensitive yang sebaliknya sama dengan pemeriksaan kolom. Gunakan kueri berikut untuk menemukan pemeriksaan kolom (ubah nama tabel dan nama skema):

    SELECT col.*
    FROM   sys.columns col
    WHERE  col.[object_id] = OBJECT_ID(N'dbo.TableName')
    AND    col.[collation_name] IS NOT NULL;
    

    Kemudian ubah saja _CSmenjadi _CI. Jadi, Latin1_General_100_CS_ASakan menjadi Latin1_General_100_CI_AS.

    Jika kolom menggunakan pemeriksaan biner (diakhiri dengan _BINatau _BIN2), temukan pemeriksaan serupa menggunakan kueri berikut:

    SELECT *
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] LIKE N'{CurrentCollationMinus"_BIN"}[_]CI[_]%';
    

    Misalnya, dengan asumsi kolom menggunakan Japanese_XJIS_100_BIN2, lakukan ini:

    SELECT *
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] LIKE N'Japanese_XJIS_100[_]CI[_]%';
    

Untuk info lebih lanjut tentang collations, encoding, dll, silakan kunjungi: Collations Info

Solomon Rutzky
sumber
7

Tidak, hanya menggunakan LIKEtidak akan berhasil. LIKEmencari nilai yang sama persis dengan pola yang Anda berikan. Dalam hal ini LIKEhanya akan menemukan teks 'sOmeVal' dan bukan 'someval'.

Solusi praktis menggunakan LCASE()fungsi tersebut. LCASE('sOmeVal')mendapatkan string huruf kecil dari teks Anda: 'someval'. Jika Anda menggunakan fungsi ini untuk kedua sisi perbandingan Anda, ini berfungsi:

SELECT * FROM myTable WHERE LCASE(myField) LIKE LCASE('sOmeVal')

Pernyataan tersebut membandingkan dua string huruf kecil, sehingga 'sOmeVal' Anda akan cocok dengan setiap notasi lain dari 'someval' (mis. 'Someval', 'sOMEVAl' dll.).

David Hermanns
sumber
7
Di 99,9% dari instalasi SQL Server yang disusun _CI, LIKE is Insensitive.
RichardTheKiwi
1
Saat ini fungsinya disebut LOWER
David Brossard
@DavidBrossard dan David Hermanns, saya rasa itu tidak pernah ada LCASE()di SQL Server (setidaknya saya tidak bisa melihat). Saya pikir jawaban ini untuk RDBMS yang sama sekali berbeda. Silakan lihat jawaban saya untuk klarifikasi tentang perbandingan string.
Solomon Rutzky
4

Anda dapat memaksa case sensitive, mentransmisikan ke varbinary seperti itu:

SELECT * FROM myTable 
WHERE convert(varbinary, myField) = convert(varbinary, 'sOmeVal')

sumber
3
Meskipun ini berfungsi, ini bukanlah pendekatan yang disarankan. Koleksi ada untuk mengelola pengurutan dan perbandingan string.
Adam Robinson
@AdamRobinson bukankah ini tentang "perbandingan string"?
Fandango68
@ Fandango68 Ya, benar, dan Adam mengatakan bahwa collations lebih baik saat melakukan perbandingan string.
JLRishe
@ Fandango68 Jawaban ini salah pada beberapa tingkatan. Tolong lihat jawaban saya untuk detailnya, terutama poin 5.
Solomon Rutzky
@AdamRobinson Silakan lihat jawaban saya untuk klarifikasi tentang perbandingan string.
Solomon Rutzky
2

Anda berada di database apa? Dengan MS SQL Server, ini adalah pengaturan seluruh database, atau Anda dapat menggantinya dengan kata kunci COLLATE.

Chase Seibert
sumber
Halo. Untuk SQL Server, dalam kaitannya dengan pertanyaan ini, ini bukanlah pengaturan seluruh database atau per kueri. Silakan lihat jawaban saya untuk detailnya.
Solomon Rutzky