PostgreSQL: วิธีสร้างเคียวรี“ case-insensitive”


339

มีวิธีใดที่จะเขียนข้อความค้นหาที่ไม่ตรงตามตัวพิมพ์ใหญ่ - เล็กใน PostgreSQL เช่นฉันต้องการให้ 3 ข้อความค้นหาที่ส่งคืนผลลัพธ์เดียวกัน

SELECT id FROM groups where name='administrator'

SELECT id FROM groups where name='ADMINISTRATOR'

SELECT id FROM groups where name='Administrator'

หาก citext มาพร้อมกับการติดตั้ง Postgres ของคุณให้ลองพิมพ์ citext เป็นข้อความที่ไม่ต้องตรงตามตัวพิมพ์ใหญ่
Michael Buen

2
สำหรับผู้มาใหม่คำถามนี้ลิงก์นี้ไปยังเอกสารอย่างเป็นทางการของ postgres ประกอบด้วยคำตอบทั้งหมดที่ให้ไว้ที่นี่รวมถึงตัวเลือกอื่น ๆ
คู่ปรับ Shot

ท่านได้มอบหมายมอบหมายตอบใหม่ให้กับ @Arun โปรด มันซับซ้อนน้อยกว่ามากและไม่ดึงปัญหาหลังจากใช้งาน
zeliboba

คำตอบ:


451

ใช้ฟังก์ชันLOWERเพื่อแปลงสตริงเป็นตัวพิมพ์เล็กก่อนเปรียบเทียบ

ลองสิ่งนี้:

SELECT id 
  FROM groups
 WHERE LOWER(name)=LOWER('Administrator')

92
เป็นสิ่งสำคัญที่จะต้องทราบว่าการใช้ LOWER (หรือฟังก์ชันใด ๆ ) ในคอลัมน์เพรดิเคต - ในกรณีนี้คือ "ชื่อ" - จะทำให้ดัชนีใด ๆ ไม่สามารถค้นหาได้อีกต่อไป หากนี่เป็นตารางที่มีขนาดใหญ่หรือมีการสอบถามบ่อยครั้งอาจทำให้เกิดปัญหาได้ การเรียงตัวพิมพ์เล็ก - ใหญ่, citext หรือดัชนีตามฟังก์ชันจะปรับปรุงประสิทธิภาพ
จอร์แดน

108
หรือเพียงสร้างดัชนีเช่นนี้ CREATE INDEX idx_groups_name ON กลุ่มที่ต่ำกว่า (ชื่อ);
Daniel

19
นอกจากนี้ยังระบุvarchar_pattern_opsถ้าคุณต้องการที่ดัชนีจะทำงานร่วมกับแบบสอบถามคือLIKE 'xxx%' CREATE INDEX ix_groups_name ON groups (lower(name) varchar_pattern_ops)
sayap

10
การใช้ตัวดำเนินการ ILIKE (ดังแสดงในคำตอบอื่น ๆ ด้านล่าง) เป็นวิธีที่ง่ายกว่าแม้ว่านี่จะเป็นคำตอบที่ได้รับการโหวตมากที่สุด
Ryan

5
จะผ่านความเห็นที่นี่เป็นจำนวนมากของข้อเสนอแนะที่นี่แสดงให้เห็นมันจะทำงานILIKE but with slow responseหากต้องการเข้าถึงตารางอย่างรวดเร็วโดยอิงจากผลลัพธ์ของการคำนวณฉันขอแนะนำให้ทุกคนที่กำลังตรวจสอบเรื่องนี้ควรใช้คำตอบที่ยอมรับได้ ดูรายละเอียดเพิ่มเติมที่นี่และที่นี่
Afolabi Olaoluwa Akinwumi

231

ใช้ILIKEแทนLIKE

SELECT id FROM groups WHERE name ILIKE 'Administrator'

1
โปรดทราบว่าILIKEไฮเบอร์เนตไม่รองรับเมื่อใช้ใน Spring Boot
AnT

@AnT มันทำงานร่วมกับorg.hibernate.dialect.PostgreSQL94Dialectและ Spring Boot 2.0.6.RELEASE แต่ IntelliJ บ่นเกี่ยวกับเรื่องนี้
Samintha Kaveesh

134

วิธีการที่พบบ่อยที่สุดคือการพิมพ์เล็กหรือสตริงการค้นหาและตัวพิมพ์ใหญ่ แต่มีสองปัญหาด้วยกัน

  1. มันทำงานได้ในภาษาอังกฤษ แต่ไม่ได้อยู่ในทุกภาษา (อาจไม่ได้เป็นในภาษาส่วนใหญ่) ไม่ใช่ตัวอักษรพิมพ์เล็กทุกตัวที่มีตัวพิมพ์ใหญ่ที่ตรงกัน ไม่ใช่ตัวอักษรตัวพิมพ์ใหญ่ทุกตัวที่มีตัวพิมพ์เล็กที่สอดคล้องกัน
  2. การใช้ฟังก์ชั่นเช่นด้านล่าง () และด้านบน () จะให้การสแกนตามลำดับ ไม่สามารถใช้ดัชนีได้ ในระบบทดสอบของฉันการใช้ lower () จะใช้เวลานานกว่าแบบสอบถามประมาณ 2000 เท่าที่สามารถใช้ดัชนีได้ (ข้อมูลทดสอบมีจำนวนแถวน้อยกว่า 100k เล็กน้อย)

มีอย่างน้อยสามโซลูชันที่ใช้บ่อยซึ่งอาจมีประสิทธิภาพมากขึ้น

  1. ใช้โมดูล citextซึ่งส่วนใหญ่เลียนแบบพฤติกรรมของชนิดข้อมูลที่ไม่ตรงตามตัวพิมพ์ใหญ่ - เล็ก เมื่อโหลดโมดูลนั้นแล้วคุณสามารถสร้างดัชนีตัวพิมพ์เล็กและตัวพิมพ์ใหญ่CREATE INDEX ON groups (name::citext);ได้ (แต่ดูด้านล่าง)
  2. ใช้การเปรียบเทียบแบบ case-insensitive ชุดนี้ถูกตั้งค่าเมื่อคุณเริ่มต้นฐานข้อมูล การใช้การเรียงตามตัวพิมพ์เล็กและตัวพิมพ์ใหญ่หมายความว่าคุณสามารถยอมรับรูปแบบใดก็ได้จากรหัสลูกค้าและคุณจะยังคงแสดงผลลัพธ์ที่มีประโยชน์ (นอกจากนี้ยังหมายความว่าคุณไม่สามารถทำแบบสอบถามที่เป็นกรณี ๆ ไปได้ Duh.)
  3. สร้างดัชนีการทำงาน CREATE INDEX ON groups (LOWER(name));สร้างดัชนีโดยใช้ตัวพิมพ์เล็ก เมื่อทำอย่างนั้นแล้วคุณสามารถใช้ประโยชน์จากดัชนีที่มีคำสั่งเช่นSELECT id FROM groups WHERE LOWER(name) = LOWER('ADMINISTRATOR');หรือSELECT id FROM groups WHERE LOWER(name) = 'administrator';คุณต้องจำไว้ว่าให้ใช้ LOWER ()

โมดูล citext ไม่มีประเภทข้อมูลที่ไม่ตรงตามตัวพิมพ์ใหญ่ - เล็ก มันจะทำงานเหมือนว่าแต่ละสตริงมีค่าลดลง นั่นคือมันจะทำงานเหมือนกับว่าคุณได้โทรlower()ไปยังแต่ละสายดังที่แสดงไว้ในข้อ 3 ข้างต้น ข้อดีคือโปรแกรมเมอร์ไม่จำเป็นต้องจำสตริงตัวพิมพ์เล็ก แต่คุณต้องอ่านหัวข้อ "พฤติกรรมการเปรียบเทียบสตริง" และ "ข้อ จำกัด " ในเอกสารก่อนที่คุณจะตัดสินใจใช้ citext


1
เกี่ยวกับ # 1: มันไม่ควรมีปัญหาเพราะมันจะเป็นสองสายที่แตกต่างกัน (คิดว่ามันเหมือนการทำcol = 'a'และcol = 'b') เกี่ยวกับ # 2: ตามที่คุณพูดคุณสามารถสร้างดัชนีในนิพจน์ดังนั้นจึงไม่ใช่ปัญหา แต่ฉันเห็นด้วยกับคุณว่าการเปลี่ยนการเรียงอาจเป็นทางออกที่ดีที่สุด
Vincent Savard

5
มีคนบอกฉันได้หรือไม่ว่าการเปรียบเทียบแบบตัวพิมพ์เล็กและใหญ่คือการเรียงข้อมูลภายในแบบ PostgreSQL ฉันเห็นสิ่งนี้เป็นตัวเลือก แต่ไม่สามารถหาอะไรเกี่ยวกับการเปรียบเทียบแบบ case-insensitive สำหรับ Postgres บนเน็ต?
khorvat

1
@AnupShah: ไม่ฉันไม่ได้พูดอย่างนั้น ฉันไม่ได้ใช้ PostgreSQL บน Windows 9.4 เอกสารบอกว่าสิ่งนี้ : "ในทุกแพลตฟอร์มการเปรียบเทียบชื่อที่มีค่าเริ่มต้น C และ POSIX จะพร้อมใช้งานการเปรียบเทียบเพิ่มเติมอาจพร้อมใช้งานทั้งนี้ขึ้นอยู่กับระบบปฏิบัติการที่รองรับ" คุณสามารถดูเรียง PostgreSQL select * from pg_collation;คิดว่าจะสามารถใช้ได้กับ
Mike Catrill 'Cat Recall'

1
@ Matthieu: นี่เป็นการแนะนำที่ดีที่สุด (และข้อควรระวัง) สำหรับหัวข้อที่ฉันรู้เกี่ยวกับ: เคสที่ควรทราบ ส่วนที่ 1 - ข้อความ
Mike Sherrill 'Cat Recall'


95

ILIKEคุณสามารถใช้ กล่าวคือ

SELECT id FROM groups where name ILIKE 'administrator'

ถูกต้องและทำงานได้ดีสำหรับฉันฉันใช้ MAC OS X (Mountain Lion)
ADJ

5
สิ่งนี้จะใช้ได้ แต่มีการตอบสนองช้า เพื่อให้สามารถเข้าถึงตารางได้อย่างรวดเร็วโดยอิงจากผลลัพธ์ของการคำนวณฉันแนะนำให้ใช้lowerฟังก์ชัน ดูรายละเอียดเพิ่มเติม
Afolabi Olaoluwa Akinwumi

1
@AfolabiOlaoluwaAkinwumi พื้นฐานนี้มาลงไปว่าคุณกำลังค้นหาเพื่อให้ได้ผลลัพธ์เทียบกับการกรอง ที่รู้จักกันค่า ในกรณีหลังควรมีกรณีเครื่องแบบเดียวที่ระดับข้อมูลที่อนุญาตให้ผู้ประกอบการความเท่าเทียมกันในการทำงาน [คำแนะนำส่วนตัวเป็นตัวอักษรภาษาปาสกาลด้านบนสำหรับค่ารหัสประเภท]
Chris Marisic

53

คุณสามารถอ่านILIKEคำสำคัญได้ บางครั้งมันก็มีประโยชน์มากแม้ว่ามันจะไม่เป็นไปตามมาตรฐาน SQL ดูที่นี่สำหรับข้อมูลเพิ่มเติม: http://www.postgresql.org/docs/9.2/static/functions-matching.html


9
สิ่งที่ต้องระวังในที่นี้คือการป้อนข้อมูลของผู้ใช้ที่เป็นอันตราย หากคุณเรียกใช้คิวรีคุณemail ILIKE 'user-input-email-here'ต้องหลีกเลี่ยงการป้อนข้อมูลผู้ใช้ มิฉะนั้นผู้คนสามารถป้อนตัวอักษรเช่น% ที่ตรงกับอะไรก็ได้
Matt De Leon

2
@MattDeLeon สวัสดี พูดได้ดี. แต่ฉันแค่อยากจะถามคุณถ้าฉันใช้ILIKEและprepared statementsสิ่งนี้จะปกป้องฉันจากsql injection?
slevin

ไม่แน่ใจฉันคิดว่าคุณต้องการส่งสตริง escape ให้กับข้อความสั่งที่เตรียมไว้
Matt De Leon

1
"คำสำคัญ ILIKE สามารถใช้แทน LIKE เพื่อทำให้ตัวพิมพ์เล็กและตัวพิมพ์เล็กตรงตามโลแคลที่ใช้งานอยู่ซึ่งไม่ได้อยู่ในมาตรฐาน SQL แต่เป็นส่วนขยาย PostgreSQL" ทำงานเหมือนมีเสน่ห์ใน 9.3
Aleksey Deryagin

1
iLike lower(column_name) like %expression%จะช้ากว่า
Patryk Imosa

28

คุณสามารถใช้นิพจน์ปกติ POSIX ได้เช่น

SELECT id FROM groups where name ~* 'administrator'

SELECT 'asd' ~* 'AsD' ผลตอบแทน t


1
ฉันมีปัญหาเดียวกันฉันต้องการค้นหาแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ในฐานข้อมูล PostgreSQL ของฉัน ฉันคิดถึงการแปลงสตริงอินพุตของผู้ใช้เป็นนิพจน์ทั่วไป ตอนนี้ใช้ ~ * แทนที่จะเป็น = หรือ LIKE ทำงานได้อย่างสมบูรณ์แบบ! ฉันไม่จำเป็นต้องสร้างดัชนีใหม่คอลัมน์หรืออะไรก็ตาม แน่นอนว่าการค้นหา regex ช้ากว่าการเปรียบเทียบแบบไบท์โดยตรง แต่ฉันไม่คิดว่าผลกระทบต่อประสิทธิภาพจะยิ่งใหญ่กว่าการจัดการข้อมูลสองชุด (หนึ่งหรือต่ำกว่าตัวพิมพ์ใหญ่เพียงเพื่อการค้นหาแล้วต้องดึงต้นฉบับที่สอดคล้องกัน ข้อมูลจากชุดอื่น ๆ ) นอกจากนี้จะสะอาดกว่า!
Cyberknight

1
ไม่เป็นไร แต่จะทำอย่างไรกับ regexp_matches () เช่น?
WKT

ตาม postgres docs: ตัวดำเนินการ ~~ เทียบเท่ากับ LIKE และ ~~ * สอดคล้องกับ ILIKE นอกจากนี้ยังมีตัวดำเนินการ! ~~ และ! ~~ * ที่แสดงว่าไม่เหมือนและไม่เหมือนกันตามลำดับ ตัวดำเนินการทั้งหมดเหล่านี้เฉพาะ PostgreSQL
sh4

ฉันประสบปัญหาเมื่อมีวงเล็บอยู่ในข้อความ แต่มันไม่ทำงาน
ไลค์

8

การใช้~*สามารถปรับปรุงประสิทธิภาพอย่างมากด้วยฟังก์ชันของ INSTR

SELECT id FROM groups WHERE name ~* 'adm'

ส่งคืนแถวที่มีชื่อที่มี OR เท่ากับ 'adm'


1
เฮ้โรบินยินดีต้อนรับสู่ SO คำตอบของ James Brown ได้เสนอทางออกนี้แล้ว นอกจากนี้คำตอบที่คุณเสนอจะไม่ใช้ประโยชน์จาก regex แต่อย่างใด
ราฟาเอล
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.