Tác động của LC_CTYPE trên cơ sở dữ liệu PostgreSQL là gì?


25

Vì vậy, tôi có vài máy chủ Debian có PostgreSQL trên đó. Trong lịch sử, các máy chủ và PostgreSQL được bản địa hóa với bộ ký tự Latin 9 và sau đó nó vẫn ổn. Bây giờ chúng tôi phải xử lý những thứ như Ba Lan, Hy Lạp hoặc Trung Quốc, vì vậy thay đổi nó trở thành một vấn đề ngày càng tăng.

Khi tôi cố gắng tạo cơ sở dữ liệu UTF8, tôi nhận được thông báo:

LRI: mã hóa UTF8 không khớp với ngôn ngữ địa phương Chi tiết: Cài đặt LC_CTYPE được chọn yêu cầu mã hóa LATIN9.

Vài lần tôi đã thực hiện một số nghiên cứu về chủ đề này với Google cũ của tôi và tất cả những gì tôi có thể tìm thấy là một số quy trình quá phức tạp như cập nhật Debian LANG, biên dịch lại PostgreQuery với bộ ký tự chính xác, chỉnh sửa tất cả các LC_biến hệ thống và các giải pháp tối nghĩa khác. Vì vậy, trong thời gian này, chúng tôi để vấn đề này sang một bên.

Gần đây, nó đã trở lại một lần nữa, người Hy Lạp muốn những thứ đó và Latin 9 không muốn. Và trong khi tôi đang xem xét vấn đề này một lần nữa, một đồng nghiệp đã đến gặp tôi và nói rằng Nah, thật dễ dàng, nhìn đi.

Anh ta không chỉnh sửa gì, không làm trò ảo thuật, anh ta chỉ thực hiện truy vấn SQL này:

CREATE DATABASE my_utf8_db
  WITH ENCODING='UTF8'
       OWNER=admin
       TEMPLATE=template0
       LC_COLLATE='C'
       LC_CTYPE='C'
       CONNECTION LIMIT=-1
       TABLESPACE=pg_default;

Và nó hoạt động tốt.

Tôi thực sự không biết LC_CTYPE='C'và tôi đã ngạc nhiên khi sử dụng nó không phải là giải pháp đầu tiên trên Google và thậm chí trên Stack Overflow. Tôi nhìn xung quanh và tôi chỉ tìm thấy một đề cập trên tài liệu PostgreSQL.

Khi LC_CTYPE là C hoặc POSIX, mọi bộ ký tự đều được cho phép, nhưng đối với các cài đặt khác của LC_CTYPE, chỉ có một bộ ký tự sẽ hoạt động chính xác. Do cài đặt LC_CTYPE bị đóng băng bởi initdb, nên tính linh hoạt rõ ràng để sử dụng các mã hóa khác nhau trong các cơ sở dữ liệu khác nhau của cụm là lý thuyết hơn thực tế, ngoại trừ khi bạn chọn ngôn ngữ C hoặc POSIX (do đó vô hiệu hóa bất kỳ nhận thức địa phương thực nào).

Vì vậy, nó làm tôi tự hỏi, điều này là quá dễ dàng, quá hoàn hảo, nhược điểm là gì? Và tôi đã có một thời gian khó khăn để tìm một câu trả lời. Vì vậy, ở đây tôi đến đăng ở đây:

tl; dr: Nhược điểm của việc sử dụng LC_CTYPE='C'trên một địa phương hóa cụ thể là gì? Làm nó là xấu? Tôi nên làm gì để phá vỡ?

Câu trả lời:


25

Nhược điểm của việc sử dụng LC_CTYPE = 'C' so với nội địa hóa cụ thể là gì

Tài liệu đề cập đến mối quan hệ giữa các tính năng cục bộ và SQL trong Hỗ trợ ngôn ngữ :

Các cài đặt ngôn ngữ ảnh hưởng đến các tính năng SQL sau:

  • Sắp xếp thứ tự trong các truy vấn bằng ORDER BY hoặc các toán tử so sánh chuẩn trên dữ liệu văn bản

  • Các hàm trên, dưới và initcap

  • Các toán tử khớp mẫu (các biểu thức chính quy kiểu THÍCH, SIMILAR TO và POSIX); các vị trí ảnh hưởng đến cả khớp không phân biệt chữ hoa chữ thường và phân loại các ký tự theo các biểu thức chính quy của lớp ký tự

  • Họ hàm to_char

  • Khả năng sử dụng các chỉ mục với mệnh đề THÍCH

Mục đầu tiên (thứ tự sắp xếp) là về LC_COLLATEvà những thứ khác dường như là tất cả về LC_CTYPE.

LC_COLLATE

LC_COLLATEảnh hưởng đến sự so sánh giữa các chuỗi. Trong thực tế, hiệu ứng rõ ràng nhất là thứ tự sắp xếp. LC_COLLATE='C'(hoặc POSIXlà một từ đồng nghĩa) có nghĩa là thứ tự byte dẫn đến sự so sánh, trong khi đó một ngôn ngữ ở language_REGIONdạng có nghĩa là các quy tắc văn hóa sẽ thúc đẩy sự so sánh.

Một ví dụ với tên tiếng Pháp, được thực hiện từ bên trong cơ sở dữ liệu UTF-8:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
 AS l(firstname)
order by firstname collate "fr_FR";

Kết quả:

 tên đầu tiên 
-----------
 béatrice
 bérénice
 bernard
 boris

béatriceđến trước boris, bởi vì E có dấu so sánh với O như thể nó không có dấu. Đó là một quy tắc văn hóa.

Điều này khác với những gì xảy ra với một Cmiền địa phương:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris')) 
 AS l(firstname)
order by firstname collate "C";

Kết quả:

 tên đầu tiên 
-----------
 bernard
 boris
 béatrice
 bérénice

Bây giờ, tên có dấu E được nhấn vào cuối danh sách. Biểu diễn byte của éUTF-8 là thập lục phân C3 A9và cho o6f. c3là lớn hơn 6fnhư vậy dưới Cmiền địa phương 'béatrice' > 'boris',.

Đó không chỉ là điểm nhấn. Có một quy tắc phức tạp hơn với dấu gạch nối, dấu chấm câu và các ký tự lạ như thế nào œ. Các quy tắc văn hóa kỳ lạ sẽ được mong đợi ở mọi địa phương.

Bây giờ nếu các chuỗi để so sánh xảy ra để trộn các ngôn ngữ khác nhau, vì khi có một firstnamecột cho mọi người từ khắp nơi trên thế giới, thì dù sao, bất kỳ ngôn ngữ cụ thể nào cũng không nên thống trị, vì các bảng chữ cái khác nhau cho các ngôn ngữ khác nhau không được thiết kế sắp xếp với nhau

Trong trường hợp Cnày là một lựa chọn hợp lý, và nó có lợi thế là nhanh hơn, bởi vì không có gì có thể vượt qua các so sánh byte thuần túy.

LC_CTYPE

Đã LC_CTYPEđặt thành 'C' ngụ ý rằng các hàm C thích isupper(c)hoặc chỉ tolower(c)đưa ra kết quả mong đợi cho các ký tự trong phạm vi US-ASCII (nghĩa là tối đa hóa mã 0x7F bằng Unicode).

Bởi vì SQL chức năng thích upper(), lower()hoặc initcap được thực hiện trong Postgres trên đầu trang của các chức năng này libc, họ đang bị ảnh hưởng bởi điều này càng sớm càng có những nhân vật phi US-ASCII trong chuỗi.

Thí dụ:

test=> show lc_ctype;
  lc_ctype   
-------------
 fr_FR.UTF-8
(1 row)

-- Good result
test=> select initcap('élysée');
 initcap 
---------
 Élysée
(1 row)

-- Wrong result
-- collate "C" is the same as if the db has been created with lc_ctype='C'
test=> select initcap('élysée' collate "C");
 initcap 
---------
 éLyséE
(1 row)

Đối với Cmiền địa phương, éđược coi là một nhân vật không thể phân loại.

Kết quả sai tương tự cũng thu được với các biểu thức thông thường:

test=> select 'élysée' ~ '^\w+$';
 ?column? 
----------
 t
(1 row)

test=> select 'élysée' COLLATE "C" ~ '^\w+$';
 ?column? 
----------
 f
(1 row)

Vì vậy, nếu tôi hiểu đúng, chúng tôi sẽ gặp sự cố về đơn hàng ngay cả khi bạn tạo máy chủ UTF-8? Tôi đoán rằng việc đặt LC_CTYPE của hệ thống trên UTF-8 hoặc biên dịch PostgreQuery trong UTF-8 sẽ dẫn đến vấn đề so sánh tương tự như bạn chỉ ra.
Gregoire D.

Để mở rộng về điều này, có thể buộc đối chiếu trên các truy vấn để so sánh sẽ là cục bộ phải không?
Gregoire D.

Có, so sánh chuỗi bất biến có thể nhúng các quy tắc đối chiếu của riêng họ, như tôi làm trong câu trả lời này với collate "C"sau order by. Tùy thuộc vào bạn để xác định xem ứng dụng của bạn cần ở đâu và ở đâu. Hầu hết các ứng dụng ngoài kia không thực sự quan tâm.
Daniel Vérité

1
Cũng lưu ý rằng các cột riêng lẻ có thể có một bộ COLLATExác định khác với cơ sở dữ liệu.
Daniel Vérité

2
Câu trả lời này thực sự dành cho LC_COLLATE, không phải LC_CTYPE. LC_CTYPE được sử dụng để quyết định xem một ký tự có phải là chữ số, chữ cái, khoảng trắng, dấu chấm câu, v.v.
jjanes

10

Để tham khảo câu trả lời được chấp nhận của Daniel về việc sắp xếp sử dụng các đối chiếu, xin lưu ý rằng nếu bạn đang chạy PostgreQuery trên máy Mac thì đối chiếu ưa thích của bạn có thể không hoạt động như bạn mong đợi do cài đặt không phù hợp cho một số đối chiếu ở cấp hệ điều hành. Bạn có thể đọc thêm về vấn đề ở đây:

http://www.postgresql.org/message-id/4B4E845F.80906@postnewspapers.com.au

Đây không phải là sự cố cụ thể của PostgreSQL, cụ thể, mà là sự cố với cấu hình mặc định của Mac đối với cài đặt đối chiếu. Hệ thống hiện tại của tôi đang chạy PostgreSQL 9.3 trên OS X El Capitan Phiên bản 10.11 và gặp phải vấn đề này. Hệ thống của tôi trả về các kết quả truy vấn tương tự bất kể tôi sử dụng đối chiếu kiểu fr fr Ví dụ:

Sử dụng đối chiếu của fr fr

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "fr_FR";

results:
==============
bernard
boris
béatrice
bérénice

Sử dụng đối chiếu của en enUSUS:

select firstname from (values ('bernard'), ('bérénice'), ('béatrice'), ('boris'))
AS l(firstname)
order by firstname collate "en_US";

results:
==============
bernard
boris
béatrice
bérénice

Trên hệ thống của tôi, các cài đặt đối chiếu (ở cấp độ hệ điều hành), giống nhau đối với loại fr frRRRRRRR

cd /usr/share/locale
diff fr_FR.UTF-8/LC_COLLATE en_US.UTF-8/LC_COLLATE

Hy vọng thông tin bổ sung này hữu ích cho bất kỳ ai đang đọc bài viết này đang sử dụng PostgreSQL trên máy Mac gặp phải vấn đề này.


Làm thế nào tôi có thể làm cho nó hoạt động trong máy Mac hiện đại. Bạn đã trải qua bất cứ điều gì để làm cho nó hoạt động trong máy Mac của bạn?
Dinesh Kumar
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.