เซิร์ฟเวอร์ SQL ละเว้นกรณีในนิพจน์ where


91

ฉันจะสร้างแบบสอบถาม SQL (MS SQL Server) ได้อย่างไรโดยที่ส่วนคำสั่ง "where" ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่

SELECT * FROM myTable WHERE myField = 'sOmeVal'

ฉันต้องการให้ผลลัพธ์กลับมาโดยไม่สนใจคดี

คำตอบ:


137

ในการกำหนดค่าเริ่มต้นของฐานข้อมูล SQL Server การเปรียบเทียบสตริงจะไม่คำนึงถึงขนาดตัวพิมพ์ หากฐานข้อมูลของคุณแทนที่การตั้งค่านี้ (ผ่านการใช้การเปรียบเทียบทางเลือก) คุณจะต้องระบุประเภทของการเปรียบเทียบที่จะใช้ในการสืบค้นของคุณ

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

โปรดทราบว่าการจัดเรียงที่ฉันให้ไว้เป็นเพียงตัวอย่าง (แม้ว่ามันจะทำงานได้ดีสำหรับคุณมากกว่าก็ตาม) เค้าร่างอย่างละเอียดมากขึ้นของ collations SQL Server สามารถพบได้ที่นี่


เพื่อยืนยันสิ่งนี้จะต้องเพิ่มเพียงครั้งเดียวในตอนท้ายของWHEREคำสั่งและจะมีผลต่อWHEREข้อทั้งหมดใช่ไหม
ashleedawg

อยากทราบว่าคำตอบของคุณมีปัญหาด้านประสิทธิภาพหรือไม่โดยการแปลงค่าคอลัมน์เป็นUPPERหรือLOWERกรณีจากนั้นใช้LIKEเพื่อค้นหา
Shaiju T

1
@ashleedawg - คำถามที่ดี .. ดูเหมือนว่าจะเป็นการตั้งค่าต่อบรรทัด
Leo Gurdian

30

โดยปกติการเปรียบเทียบสตริงจะไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ หากฐานข้อมูลของคุณถูกกำหนดค่าให้เรียงตามตัวพิมพ์เล็กและใหญ่คุณต้องบังคับให้ใช้ตัวพิมพ์เล็กและใหญ่:

SELECT balance FROM people WHERE email = 'billg@microsoft.com'
  COLLATE SQL_Latin1_General_CP1_CI_AS 

@ AskeB. และ Andrejs: นี่ไม่ใช่ปัญหาการกำหนดค่าฐานข้อมูลในทางเทคนิค โปรดดูคำตอบของฉันสำหรับคำชี้แจงเกี่ยวกับการเปรียบเทียบสตริง
Solomon Rutzky

21

ฉันพบวิธีแก้ปัญหาอื่นที่อื่น นั่นคือการใช้

upper(@yourString)

แต่ทุกคนที่นี่บอกว่าใน SQL Server มันไม่สำคัญหรอกเพราะมันไม่สนใจเคสอยู่ดี? ฉันค่อนข้างมั่นใจว่าฐานข้อมูลของเราคำนึงถึงขนาดตัวพิมพ์


7
คุณถูกต้องที่ฐานข้อมูลสามารถทำให้ตรงตามตัวพิมพ์เล็กและใหญ่ แต่ก็ไม่มีประสิทธิภาพแม้ว่าจะจำเป็นก็ตาม COLLATE คือคีย์เวิร์ดที่จะใช้
mjaggard

1
ขอบคุณที่แจ้งเรื่องนี้ @mjaggard ฉันหวังว่าคุณหรือใครก็ตามที่ดูเหมือนจะลดคะแนนคำตอบของฉันจะอธิบายอย่างละเอียดเพื่อประโยชน์ของใครก็ตามเช่นตัวฉันที่ค้นหาและพบคำตอบเช่นเดียวกับฉัน
Danny

1
เพิ่มคะแนนสิ่งนี้เนื่องจากเป็นคำอธิบายที่มีเหตุผลอย่างสมบูรณ์ จัดเรียงค่าใช้จ่ายที่มากเกินไปและจะเกิดอะไรขึ้นถ้าสตริงของคุณมีอักขระที่การเรียงไม่เข้าใจ ละติน 1 เป็นรูปแบบการเข้ารหัสที่มีหมัด ขอให้โชคดีที่ได้ผลลัพธ์ที่มีความหมายหากสตริงของคุณมีเครื่องหมายวรรคตอนอยู่ (เช่น: O'Brien)
ตีไข่

2
โหวตแล้วด้วย ฉันนึกถึงกรณีต่างๆมากมายที่จะเป็นประโยชน์ นอกจากนี้มักมีวิธีที่ดีมากกว่าหนึ่งวิธีในการทำบางสิ่ง
Inversus

1
โดยทั่วไปการเปลี่ยนกรณีของสตริงเพื่อจุดประสงค์ในการเปรียบเทียบนั้นไม่ดี ในบางภาษากรณีที่การแปลงไม่ได้ไป - กลับ เช่น LOWER (x)! = LOWER (บน (x))
Ceisc

18

คำตอบ 2 อันดับแรก (จากAdam RobinsonและAndrejs Cainikovs ) เป็นคำตอบที่ถูกต้องเนื่องจากใช้งานได้ในทางเทคนิค แต่คำอธิบายของพวกเขาผิดและอาจทำให้เข้าใจผิดได้ในหลาย ๆ กรณี ตัวอย่างเช่นแม้ว่าการSQL_Latin1_General_CP1_CI_ASเปรียบเทียบจะทำงานในหลาย ๆ กรณี แต่ก็ไม่ควรถือว่าเป็นการเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ที่เหมาะสม ในความเป็นจริงเนื่องจาก OP ทำงานในฐานข้อมูลที่มีการเปรียบเทียบตัวพิมพ์เล็กและใหญ่ (หรืออาจเป็นไบนารี) เราจึงทราบว่า OP ไม่ได้ใช้การเปรียบเทียบซึ่งเป็นค่าเริ่มต้นสำหรับการติดตั้งจำนวนมาก SQL_Latin1_General_CP1_CI_ASโดยใช้ภาษาอังกฤษเป็นภาษา): แน่นอนว่า OP สามารถใช้SQL_Latin1_General_CP1_CS_ASงานได้ แต่เมื่อทำงานกับไฟล์VARCHARข้อมูลเป็นสิ่งสำคัญที่จะต้องไม่เปลี่ยนโค้ดเพจเนื่องจากอาจทำให้ข้อมูลสูญหายและถูกควบคุมโดยโลแคล / วัฒนธรรมของการจัดเรียง (เช่น Latin1_General vs French vs Hebrew เป็นต้น) โปรดดูจุด # 9 ด้านล่าง

อีกสี่คำตอบนั้นผิดองศาที่แตกต่างกัน

ฉันจะชี้แจงความเข้าใจผิดทั้งหมดที่นี่เพื่อให้ผู้อ่านสามารถตัดสินใจเลือกที่เหมาะสม / มีประสิทธิภาพมากที่สุด

  1. ห้ามใช้UPPER(). นั่นคืองานพิเศษที่ไม่จำเป็นอย่างสิ้นเชิง ใช้COLLATEอนุประโยค. ต้องทำการเปรียบเทียบสตริงในทั้งสองกรณี แต่การใช้UPPER()ยังต้องตรวจสอบทีละอักขระเพื่อดูว่ามีการแม็ปตัวพิมพ์ใหญ่หรือไม่จากนั้นจึงเปลี่ยน และคุณต้องทำทั้งสองด้าน การเพิ่มCOLLATEเพียงแค่สั่งให้การประมวลผลสร้างคีย์การจัดเรียงโดยใช้ชุดของกฎที่แตกต่างจากที่เป็นไปตามค่าเริ่มต้น ใช้COLLATEแน่นอนมีประสิทธิภาพมากขึ้น (หรือ "performant" ถ้าคุณชอบคำว่า :) กว่าการใช้UPPER()เช่นการพิสูจน์ในการนี้สคริปต์ทดสอบ (บน Pastebin)

    นอกจากนี้ยังมีปัญหาที่ระบุโดย @Ceisc ในคำตอบของ @ Danny:

    ในบางภาษากรณีที่การแปลงไม่ได้ไป - กลับ เช่น LOWER (x)! = LOWER (บน (x))

    ตัวพิมพ์ใหญ่ของตุรกี "İ" เป็นตัวอย่างทั่วไป

  2. ไม่การจัดเรียงไม่ใช่การตั้งค่าทั้งฐานข้อมูลอย่างน้อยก็ไม่ใช่ในบริบทนี้ มีการเปรียบเทียบเริ่มต้นในระดับฐานข้อมูลและใช้เป็นค่าเริ่มต้นสำหรับคอลัมน์ที่เปลี่ยนแปลงและสร้างขึ้นใหม่ที่ไม่ระบุส่วนCOLLATEคำสั่ง (ซึ่งเป็นไปได้ว่าความเข้าใจผิดทั่วไปนี้มาจากที่ใด) แต่จะไม่ส่งผลต่อการสืบค้นโดยตรงเว้นแต่คุณจะเป็น เปรียบเทียบลิเทอรัลสตริงและตัวแปรกับลิเทอรัลและตัวแปรสตริงอื่น ๆ หรือคุณกำลังอ้างอิงข้อมูลเมตาดาต้าระดับฐานข้อมูล

  3. ไม่การเรียงลำดับไม่ใช่การค้นหา

  4. การเรียงต่อกันเป็นไปตามเพรดิเคต (เช่นบางสิ่งที่ถูกดำเนินการบางอย่าง) หรือนิพจน์ไม่ใช่ต่อแบบสอบถาม และนี่เป็นจริงสำหรับข้อความค้นหาทั้งหมดไม่ใช่เฉพาะWHEREอนุประโยค ซึ่งครอบคลุมถึง JOINs, GROUP BY, ORDER BY, PARTITION BY ฯลฯ

  5. ไม่อย่าแปลงเป็นVARBINARY(เช่นconvert(varbinary, myField) = convert(varbinary, 'sOmeVal')) ด้วยเหตุผลต่อไปนี้:

    1. นั่นคือการเปรียบเทียบแบบไบนารีซึ่งไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ (ซึ่งเป็นสิ่งที่คำถามนี้ต้องการ)
    2. หากคุณต้องการเปรียบเทียบแบบไบนารีให้ใช้การเปรียบเทียบแบบไบนารี ใช้อย่างใดอย่างหนึ่งที่ลงท้ายด้วย_BIN2ถ้าคุณกำลังใช้ SQL Server 2008 หรือใหม่กว่าอื่น ๆ ที่คุณไม่มีทางเลือก _BINแต่จะใช้หนึ่งที่ลงท้ายด้วย หากข้อมูลเป็นเช่นNVARCHARนั้นก็ไม่สำคัญว่าคุณจะใช้โลแคลใดเนื่องจากจะเหมือนกันทั้งหมดในกรณีนั้นจึงLatin1_General_100_BIN2ใช้ได้เสมอ ถ้าข้อมูลVARCHARคุณต้องใช้สถานที่เดียวกันว่าข้อมูลที่เป็นอยู่ในปัจจุบัน (เช่นLatin1_General, French, Japanese_XJISฯลฯ ) เพราะสถานที่เกิดเหตุเป็นตัวกำหนดหน้ารหัสที่ใช้และการเปลี่ยนแปลงโค้ดเพจสามารถแก้ไขข้อมูล (เช่นการสูญเสียข้อมูล)
    3. การใช้ประเภทข้อมูลที่มีความยาวผันแปรโดยไม่ระบุขนาดจะขึ้นอยู่กับขนาดเริ่มต้นและมีค่าเริ่มต้นที่แตกต่างกันสองค่าขึ้นอยู่กับบริบทที่ใช้ประเภทข้อมูล เป็น 1 หรือ 30 สำหรับประเภทสตริง เมื่อใช้กับCONVERT()มันจะใช้ค่าเริ่มต้น 30 อันตรายคือถ้าสตริงมีขนาดมากกว่า 30 ไบต์สตริงจะถูกตัดทอนอย่างเงียบ ๆ และคุณอาจได้ผลลัพธ์ที่ไม่ถูกต้องจากเพรดิเคตนี้
    4. แม้ว่าคุณจะต้องการการเปรียบเทียบแบบคำนึงถึงตัวพิมพ์เล็กและใหญ่ แต่การเปรียบเทียบแบบไบนารีนั้นไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ (อีกความเข้าใจผิดที่พบบ่อยมาก)
  6. ไม่LIKEไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่เสมอไป ใช้การเรียงของคอลัมน์ที่อ้างอิงหรือการเปรียบเทียบของฐานข้อมูลหากเปรียบเทียบตัวแปรกับสตริงลิเทอรัลหรือการเปรียบเทียบที่ระบุผ่านส่วนCOLLATEคำสั่งเสริม

  7. LCASEไม่ใช่ฟังก์ชัน SQL Server ดูเหมือนว่าจะเป็น Oracle หรือ MySQL หรืออาจเป็น Visual Basic?

  8. เนื่องจากบริบทของคำถามกำลังเปรียบเทียบคอลัมน์กับสตริงลิเทอรัลทั้งการเปรียบเทียบอินสแตนซ์ (มักเรียกว่า "เซิร์ฟเวอร์") หรือการเปรียบเทียบฐานข้อมูลไม่มีผลกระทบโดยตรงที่นี่ การเรียงลำดับจะถูกเก็บไว้ในแต่ละคอลัมน์และแต่ละคอลัมน์สามารถมีการเปรียบเทียบที่แตกต่างกันได้และการเปรียบเทียบเหล่านั้นไม่จำเป็นต้องเหมือนกับการเปรียบเทียบเริ่มต้นของฐานข้อมูลหรือการเปรียบเทียบของอินสแตนซ์ แน่นอนว่าการเปรียบเทียบอินสแตนซ์เป็นค่าเริ่มต้นสำหรับสิ่งที่ฐานข้อมูลที่สร้างขึ้นใหม่จะใช้เป็นการเปรียบเทียบเริ่มต้นหากCOLLATEไม่ได้ระบุอนุประโยคเมื่อสร้างฐานข้อมูล และในทำนองเดียวกันการเปรียบเทียบเริ่มต้นของฐานข้อมูลคือสิ่งที่คอลัมน์ที่เปลี่ยนแปลงหรือสร้างขึ้นใหม่จะใช้หากCOLLATEไม่ได้ระบุอนุประโยค

  9. คุณควรใช้การเปรียบเทียบแบบไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ซึ่งเหมือนกับการเปรียบเทียบคอลัมน์ ใช้แบบสอบถามต่อไปนี้เพื่อค้นหาการเรียงของคอลัมน์ (เปลี่ยนชื่อตารางและชื่อสคีมา):

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

    แล้วก็เปลี่ยนให้เป็น_CS _CIดังนั้นก็จะกลายเป็นLatin1_General_100_CS_ASLatin1_General_100_CI_AS

    หากคอลัมน์กำลังใช้การเปรียบเทียบแบบไบนารี (ลงท้ายด้วย_BINหรือ_BIN2) ให้ค้นหาการเปรียบเทียบที่คล้ายกันโดยใช้แบบสอบถามต่อไปนี้:

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

    ตัวอย่างเช่นสมมติว่ากำลังใช้คอลัมน์Japanese_XJIS_100_BIN2ให้ทำสิ่งนี้:

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

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการจัดเรียงการเข้ารหัสและอื่น ๆ โปรดไปที่: ข้อมูลการจัดเรียง


7

ไม่ใช้เพียงอย่างเดียวLIKEจะไม่ทำงาน LIKEค้นหาค่าที่ตรงกับรูปแบบที่คุณกำหนด ในกรณีนี้LIKEจะพบเฉพาะข้อความ "sOmeVal" และไม่ใช่ "someval"

โซลูชันที่ใช้งานได้จริงคือการใช้LCASE()ฟังก์ชัน LCASE('sOmeVal')รับสตริงตัวพิมพ์เล็กของข้อความของคุณ: "someval" หากคุณใช้ฟังก์ชันนี้สำหรับการเปรียบเทียบทั้งสองด้านจะได้ผล:

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

คำสั่งจะเปรียบเทียบสตริงตัวพิมพ์เล็กสองตัวเพื่อให้ 'sOmeVal' ของคุณตรงกับสัญกรณ์อื่น ๆ ของ 'someval' (เช่น 'Someval', 'sOMEVAl' เป็นต้น)


7
ใน 99.9% ของการติดตั้ง SQL Server ที่เรียงกัน _CI LIKE ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่
RichardTheKiwi

1
ปัจจุบันฟังก์ชันนี้เรียกว่า LOWER
David Brossard

@DavidBrossard และ David Hermanns ฉันไม่คิดว่ามันเคยLCASE()อยู่ใน SQL Server (อย่างน้อยก็ไม่ใช่ที่ฉันเห็น) ฉันคิดว่าคำตอบนี้มีไว้สำหรับ RDBMS ที่แตกต่างกันอย่างสิ้นเชิง โปรดดูคำตอบของฉันสำหรับคำชี้แจงเกี่ยวกับการเปรียบเทียบสตริง
Solomon Rutzky

4

คุณสามารถบังคับให้พิจารณาตัวพิมพ์เล็กและใหญ่โดยหล่อเป็นตัวแปรแบบนั้น

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

3
แม้ว่าวิธีนี้จะใช้งานได้ แต่ก็ไม่ใช่แนวทางที่แนะนำ Collations มีไว้สำหรับจัดการการเรียงลำดับและการเปรียบเทียบสตริง
Adam Robinson

@AdamRobinson ไม่เกี่ยวกับ "การเปรียบเทียบสตริง" เหรอ?
Fandango68

@ Fandango68 ใช่แล้วและอดัมบอกว่าการเปรียบเทียบจะดีกว่าเมื่อทำการเปรียบเทียบสตริง
JLRishe

@ Fandango68 คำตอบนี้ไม่ถูกต้องในหลายระดับ โปรดดูคำตอบของฉันสำหรับรายละเอียดโดยเฉพาะจุดที่ 5
Solomon Rutzky

@AdamRobinson โปรดดูคำตอบของฉันเพื่อความกระจ่างเกี่ยวกับการเปรียบเทียบสตริง
Solomon Rutzky

2

คุณอยู่บนฐานข้อมูลใด ด้วย MS SQL Server เป็นการตั้งค่าทั่วทั้งฐานข้อมูลหรือคุณสามารถใช้งานต่อแบบสอบถามด้วยคีย์เวิร์ด COLLATE


สวัสดี. สำหรับ SQL Server ในแง่ของคำถามนี้ไม่ใช่ทั้งการตั้งค่าทั้งฐานข้อมูลหรือต่อแบบสอบถาม โปรดดูคำตอบของฉันสำหรับรายละเอียด
Solomon Rutzky
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.