PostgreSQL: Cách tạo truy vấn không phân biệt chữ hoa chữ thường


338

Có cách nào để viết các truy vấn không phân biệt chữ hoa chữ thường trong PostgreSQL không, ví dụ tôi muốn rằng 3 truy vấn sau đó trả về cùng một kết quả.

SELECT id FROM groups where name='administrator'

SELECT id FROM groups where name='ADMINISTRATOR'

SELECT id FROM groups where name='Administrator'

nếu citext đi kèm với cài đặt Postgres của bạn, hãy thử loại citext. Đó là văn bản không phân biệt chữ hoa chữ thường
Michael Buen

2
Đối với những người mới đến câu hỏi này, liên kết này đến tài liệu postgres chính thức chứa tất cả các câu trả lời được đưa ra ở đây, cũng như một vài lựa chọn khác.
Bắn Parthian

Xin hãy xác nhận lại câu trả lời được chấp nhận cho câu trả lời của @Arun. Nó ít phức tạp hơn nhiều và không kéo theo nhiều rắc rối sau khi áp dụng.
zeliboba

Câu trả lời:


451

Sử dụng hàm LOWER để chuyển đổi các chuỗi thành chữ thường trước khi so sánh.

Thử cái này:

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

92
Điều quan trọng cần lưu ý là việc sử dụng LOWER (hoặc bất kỳ chức năng nào) trên các cột vị ngữ - trong trường hợp này là "tên" - sẽ khiến bất kỳ chỉ mục nào không còn có thể tìm kiếm được. Nếu đây là một bảng lớn hoặc thường xuyên được truy vấn, điều đó có thể gây rắc rối. Đối chiếu không phân biệt chữ hoa chữ thường, citext hoặc chỉ mục dựa trên chức năng sẽ cải thiện hiệu suất.
Jordan

108
Hoặc chỉ cần tạo một chỉ mục như thế này: CREATE INDEX idx_groups_name ON các nhóm thấp hơn (tên);
Daniel

19
Cũng xác định varchar_pattern_opsnếu bạn muốn chỉ mục hoạt động với LIKE 'xxx%'truy vấn, tức là CREATE INDEX ix_groups_name ON groups (lower(name) varchar_pattern_ops).
sayap

10
Sử dụng toán tử ILIKE (như thể hiện trong các câu trả lời khác bên dưới) là cách tiếp cận đơn giản hơn, mặc dù đây là câu trả lời được bình chọn nhiều nhất.
Ryan

5
Đi qua các ý kiến ​​ở đây, rất nhiều gợi ý ở đây cho thấy ILIKE, Nó sẽ hoạt động but with slow response,. Để có được quyền truy cập nhanh vào các bảng dựa trên kết quả tính toán, tôi khuyên mọi người chỉ cần kiểm tra điều này nên đi với câu trả lời được chấp nhận. Xem thêm chi tiết tại đâytại đây
Afolabi Olaoluwa Akinwumi

230

sử dụng ILIKEthay vìLIKE

SELECT id FROM groups WHERE name ILIKE 'Administrator'

1
Lưu ý rằng ILIKEkhông được Hibernate hỗ trợ khi được sử dụng trong Spring Boot.
AnT

@AnT nó hoạt động với org.hibernate.dialect.PostgreSQL94Dialectvà Spring Boot 2.0.6.RELEASE. Nhưng IntelliJ phàn nàn về nó.
Samintha Kaveesh

134

Cách tiếp cận phổ biến nhất là viết thường hoặc viết hoa chuỗi tìm kiếm và dữ liệu. Nhưng có hai vấn đề với điều đó.

  1. Nó hoạt động bằng tiếng Anh, nhưng không phải trong tất cả các ngôn ngữ. (Có thể thậm chí không có trong hầu hết các ngôn ngữ.) Không phải mọi chữ cái viết thường đều có một chữ cái viết hoa tương ứng; không phải chữ cái viết hoa nào cũng có chữ cái viết thường tương ứng.
  2. Sử dụng các hàm như Lower () và Upper () sẽ giúp bạn quét tuần tự. Nó không thể sử dụng các chỉ mục. Trên hệ thống thử nghiệm của tôi, việc sử dụng low () mất khoảng 2000 lần so với truy vấn có thể sử dụng một chỉ mục. (Dữ liệu thử nghiệm có hơn 100 nghìn hàng.)

Có ít nhất ba giải pháp ít được sử dụng có thể hiệu quả hơn.

  1. Sử dụng mô-đun citext , phần lớn bắt chước hành vi của kiểu dữ liệu không phân biệt chữ hoa chữ thường. Khi đã tải mô-đun đó, bạn có thể tạo một chỉ mục không phân biệt chữ hoa chữ thường CREATE INDEX ON groups (name::citext);. (Nhưng xem bên dưới.)
  2. Sử dụng đối chiếu không phân biệt chữ hoa chữ thường. Điều này được đặt khi bạn khởi tạo cơ sở dữ liệu. Sử dụng đối chiếu không phân biệt chữ hoa chữ thường có nghĩa là bạn có thể chấp nhận bất kỳ định dạng nào từ mã máy khách và bạn vẫn sẽ trả về kết quả hữu ích. (Điều đó cũng có nghĩa là bạn không thể thực hiện các truy vấn phân biệt chữ hoa chữ thường. Duh.)
  3. Tạo một chỉ mục chức năng. Tạo một chỉ mục chữ thường bằng cách sử dụng CREATE INDEX ON groups (LOWER(name));. Khi đã thực hiện điều đó, bạn có thể tận dụng chỉ mục với các truy vấn như SELECT id FROM groups WHERE LOWER(name) = LOWER('ADMINISTRATOR');hoặc SELECT id FROM groups WHERE LOWER(name) = 'administrator';bạn phải nhớ sử dụng LOWER ().

Mô-đun citext không cung cấp kiểu dữ liệu không phân biệt chữ hoa chữ thường. Thay vào đó, nó hoạt động như thể mỗi chuỗi được hạ thấp. Đó là, nó hoạt động như thể bạn đã gọi lower()trên mỗi chuỗi, như trong số 3 ở trên. Ưu điểm là các lập trình viên không phải nhớ ký tự viết thường. Nhưng bạn cần đọc các phần "Hành vi so sánh chuỗi" và "Hạn chế" trong tài liệu trước khi bạn quyết định sử dụng citext.


1
Về # 1: Nó không phải là một vấn đề, vì nó sẽ là hai chuỗi khác nhau (nghĩ về nó như làm col = 'a'col = 'b'). Về # 2: Như bạn đã nói, bạn có thể tạo một chỉ mục trên một biểu thức, vì vậy nó không thực sự là một vấn đề. Nhưng tôi đồng ý với bạn rằng thay đổi đối chiếu rất có thể là giải pháp tốt nhất.
Vincent Savard

5
Ai đó có thể cho tôi biết những bộ sưu tập không phân biệt chữ hoa chữ thường là những bộ sưu tập dựng sẵn của PostgreSQL không? Tôi thấy đây là một tùy chọn nhưng không thể tìm thấy bất cứ điều gì về đối chiếu không phân biệt chữ hoa chữ thường đối với Postgres trên mạng?
khorvat

1
@AnupShah: Không, tôi không nói như vậy. Tôi không chạy PostgreSQL trên Windows. 9,4 tài liệu nói điều này : "Trên tất cả các nền tảng, các bộ sưu tập có tên mặc định, C và POSIX đều khả dụng. Các bộ sưu tập bổ sung có thể có sẵn tùy thuộc vào hỗ trợ của hệ điều hành." Bạn có thể xem những đối chiếu mà PostgreSQL nghĩ là có sẵn select * from pg_collation;.
Mike Sherrill 'Nhớ lại mèo'

1
@Matthieu: Đây là phần giới thiệu tốt nhất (và thận trọng) cho chủ đề mà tôi biết: Các trường hợp cần ghi nhớ. Phần 1 - Văn bản .
Mike Sherrill 'Nhớ lại mèo'


95

Bạn có thể sử dụng ILIKE. I E

SELECT id FROM groups where name ILIKE 'administrator'

Nó chính xác và hoạt động tốt với tôi, tôi đang sử dụng MAC OS X (Mountain Lion).
ADJ

5
Điều này sẽ làm việc, nhưng với phản ứng chậm. Để có được quyền truy cập nhanh vào các bảng dựa trên kết quả tính toán, tôi đề nghị sử dụng lowerhàm. Xem thêm chi tiết
Afolabi Olaoluwa Akinwumi

1
@AreasabiOlaoluwaAkinwumi về cơ bản điều này phụ thuộc vào việc bạn đang tìm kiếm kết quả trái ngược với việc lọc các giá trị đã biết . Trong trường hợp sau, một trường hợp thống nhất duy nhất nên được duy trì ở mức dữ liệu cho phép toán tử đẳng thức hoạt động. [Đề xuất cá nhân là trường hợp pascal trên cho các giá trị mã loại]
Chris Marisic

53

Bạn cũng có thể đọc lên ILIKEtừ khóa. Nó có thể khá hữu ích đôi khi, mặc dù nó không phù hợp với tiêu chuẩn SQL. Xem tại đây để biết thêm thông tin: http://www.postgresql.org/docs/9.2/static/fifts-matching.html


9
Một cái gì đó để coi chừng ở đây là đầu vào của người dùng độc hại. Nếu bạn chạy một truy vấn như thế nào email ILIKE 'user-input-email-here', hãy đảm bảo thoát khỏi đầu vào của người dùng. Nếu không, mọi người có thể nhập các ký tự như% phù hợp với bất cứ điều gì.
Matt De Leon

2
@MattDeLeon Xin chào. Nói hay lắm. Nhưng tôi chỉ muốn hỏi bạn, nếu tôi sử dụng ILIKEprepared statementsđiều này sẽ bảo vệ tôi khỏi sql injection?
slevin

Không chắc chắn, tôi cho rằng bạn muốn gửi một chuỗi thoát đến câu lệnh đã chuẩn bị.
Matt De Leon

1
"Từ khóa ILIKE có thể được sử dụng thay vì THÍCH để làm cho trường hợp không phân biệt chữ hoa chữ thường theo ngôn ngữ hoạt động. Đây không phải là tiêu chuẩn SQL mà là một phần mở rộng PostgreQuery." Hoạt động như một bùa mê trong 9.3
Aleksey Deryagin 23/12/14

1
ILIKE chậm hơn lower(column_name) like %expression%.
Patryk Imosa

28

Bạn cũng có thể sử dụng biểu thức chính quy POSIX, như

SELECT id FROM groups where name ~* 'administrator'

SELECT 'asd' ~* 'AsD' trả lại t


1
Tôi đã có cùng một vấn đề, tôi cần tìm kiếm không nhạy cảm trường hợp trên cơ sở dữ liệu PostgreSQL của tôi. Tôi nghĩ về việc chuyển đổi chuỗi đầu vào của người dùng thành một biểu thức chính quy. Bây giờ, sử dụng ~ * thay vì = hoặc THÍCH đã hoạt động hoàn hảo! Tôi không cần tạo chỉ mục mới, cột hoặc bất cứ điều gì. Chắc chắn, tìm kiếm regex chậm hơn so với so sánh byte thẳng, nhưng tôi không nghĩ rằng tác động đến hiệu suất sẽ lớn hơn nhiều so với việc phải xử lý hai bộ dữ liệu (một thấp hơn hoặc cao hơn chỉ để tìm kiếm, sau đó phải truy xuất bản gốc tương ứng dữ liệu từ bộ khác). Bên cạnh đó, cái này sạch hơn!
Cyberknight

1
Tốt, nhưng làm thế nào với regrec_matches () chẳng hạn?
WKT

Theo tài liệu postgres: Toán tử ~ ~ tương đương với THÍCH và ~ ~ * tương ứng với ILIKE. Ngoài ra còn có các toán tử! ~ ~ Và! ~ ~ * Tương ứng KHÔNG THÍCH và KHÔNG ILIKE. Tất cả các toán tử này đều dành riêng cho PostgreSQL.
sh4

Tôi gặp phải một vấn đề khi dấu ngoặc được bao gồm trong văn bản, nó không hoạt động. như: "mã (LC)"
Oshan Wisumperuma

8

Việc sử dụng ~*có thể cải thiện đáng kể về hiệu suất, với chức năng của INSTR.

SELECT id FROM groups WHERE name ~* 'adm'

trả về các hàng có tên chứa OR bằng với 'adm'.


1
Này, Robin, chào mừng đến với SO. Câu trả lời của James Brown đã đề xuất giải pháp này. Ngoài ra, câu trả lời được đề xuất của bạn không thúc đẩy regex theo bất kỳ cách nào.
Rafael
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.