LC_CTYPE มีผลกระทบอะไรกับฐานข้อมูล PostgreSQL


25

ดังนั้นฉันมีเซิร์ฟเวอร์ Debian ไม่กี่เครื่องที่มี PostgreSQL อยู่ ในอดีตเซิร์ฟเวอร์เหล่านั้นและ PostgreSQL มีการแปลเป็นภาษาละตินด้วยชุดอักขระ Latin 9 และย้อนกลับไปได้ ตอนนี้เราต้องจัดการกับสิ่งต่างๆเช่นโปแลนด์กรีกหรือจีนดังนั้นการเปลี่ยนมันจึงกลายเป็นปัญหาที่กำลังเติบโต

เมื่อฉันพยายามสร้างฐานข้อมูล UTF8 ฉันได้รับข้อความ:

ข้อผิดพลาด: การเข้ารหัส UTF8 ไม่ตรงกับรายละเอียดสถานที่ fr_FR: การตั้งค่า LC_CTYPE ที่เลือกต้องใช้การเข้ารหัส LATIN9

ไม่กี่ครั้งที่ฉันได้ทำการวิจัยเกี่ยวกับเรื่องนี้กับเพื่อนเก่าของ Google และสิ่งที่ฉันพบคือขั้นตอนที่ซับซ้อนเช่นการอัปเดต Debian LANG, คอมไพล์ PostgreSQL ด้วยชุดอักขระที่ถูกต้องแก้ไขLC_ตัวแปรระบบทั้งหมดและโซลูชันที่ไม่ชัดเจนอื่น ๆ ดังนั้นในขณะนี้เราจึงปล่อยให้ปัญหานี้เกิดขึ้น

เมื่อเร็ว ๆ นี้มันกลับมาอีกครั้งชาวกรีกต้องการสิ่งของและละติน 9 ไม่ต้องการ และในขณะที่ฉันกำลังตรวจสอบปัญหานี้อีกครั้งเพื่อนร่วมงานคนหนึ่งมาหาฉันและพูดว่า "ไม่นะมันง่ายดูสิ"

เขาไม่ได้ทำการแก้ไขอะไรเลยไม่ได้ใช้กลอุบายเขาแค่ทำแบบสอบถาม SQL นี้:

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

และมันก็ใช้ได้ดี

ที่จริงฉันไม่รู้LC_CTYPE='C'และฉันประหลาดใจที่การใช้สิ่งนี้ไม่ได้เป็นโซลูชันแรกใน Google และแม้แต่ใน Stack Overflow ฉันมองไปรอบ ๆ และพบเพียงการกล่าวถึงในเอกสาร PostgreSQL

เมื่อ LC_CTYPE เป็น C หรือ POSIX อนุญาตให้ใช้ชุดอักขระใด ๆ แต่สำหรับการตั้งค่าอื่นของ LC_CTYPE จะมีชุดอักขระเพียงชุดเดียวที่ทำงานได้อย่างถูกต้อง เนื่องจากการตั้งค่า LC_CTYPE ถูกแช่แข็งโดย initdb ความยืดหยุ่นที่ชัดเจนในการใช้การเข้ารหัสที่แตกต่างกันในฐานข้อมูลที่แตกต่างกันของคลัสเตอร์นั้นมีความเป็นทฤษฎีมากกว่าของจริงยกเว้นเมื่อคุณเลือกภาษา C หรือ POSIX (ปิดใช้งานการรับรู้สถานที่จริง)

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

tl; dr: ข้อเสียของการใช้LC_CTYPE='C'มากกว่าการแปลเฉพาะคืออะไร? การทำเช่นนั้นไม่ดีใช่ไหม สิ่งที่ฉันควรคาดหวังที่จะทำลาย?

คำตอบ:


25

อะไรคือข้อเสียของการใช้ LC_CTYPE = 'C' ในการแปลที่เฉพาะเจาะจง

เอกสารอธิบายถึงความสัมพันธ์ระหว่างlocaleและคุณลักษณะ SQL ในการสนับสนุน Locale :

การตั้งค่าโลแคลส่งผลต่อคุณลักษณะ SQL ต่อไปนี้:

  • เรียงลำดับในแบบสอบถามโดยใช้ ORDER BY หรือตัวดำเนินการเปรียบเทียบมาตรฐานกับข้อมูลที่เป็นข้อความ

  • ฟังก์ชันบน, ล่างและ initcap

  • ตัวดำเนินการการจับคู่รูปแบบ (นิพจน์ปกติแบบ LIKE, SIMILAR TO และ POSIX); สถานที่มีผลต่อการจับคู่ทั้งสองกรณีและการจำแนกประเภทของตัวละครโดยการแสดงออกปกติระดับตัวละคร

  • ตระกูล to_char ของฟังก์ชัน

  • ความสามารถในการใช้ดัชนีกับ LIKE clauses

รายการแรก (เรียงลำดับ) เป็นเรื่องเกี่ยวกับLC_COLLATEและคนอื่น ๆ LC_CTYPEดูเหมือนทั้งหมดจะเกี่ยวกับ

LC_COLLATE

LC_COLLATEส่งผลกระทบต่อการเปรียบเทียบระหว่างสตริง ในทางปฏิบัติผลกระทบที่มองเห็นได้มากที่สุดคือลำดับการจัดเรียง LC_COLLATE='C'(หรือPOSIXคำพ้องความหมาย) หมายความว่าเป็นลำดับไบต์ที่ทำให้เกิดการเปรียบเทียบในขณะที่โลแคลในlanguage_REGIONรูปแบบหมายความว่ากฎทางวัฒนธรรมจะผลักดันการเปรียบเทียบ

ตัวอย่างที่มีชื่อภาษาฝรั่งเศสดำเนินการจากภายในฐานข้อมูล UTF-8:

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

ผล:

 ชื่อจริง 
-----------
 Béatrice
 Bérénice
 เบอร์นาร์ด
 บอริส

béatriceมาก่อนborisเพราะ E ที่ถูกเน้นเสียงนั้นเปรียบเทียบกับ O ราวกับว่ามันไม่ได้เน้นเสียง มันเป็นกฎทางวัฒนธรรม

สิ่งนี้แตกต่างจากสิ่งที่เกิดขึ้นกับCโลแคล:

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

ผล:

 ชื่อจริง 
-----------
 เบอร์นาร์ด
 บอริส
 Béatrice
 Bérénice

ตอนนี้ชื่อที่มีการเน้นเสียง E จะถูกผลักดันในตอนท้ายของรายการ การเป็นตัวแทนของไบต์éUTF-8 เป็นเลขฐานสิบหกC3 A9และมันเป็นo มากกว่านั้นภายใต้สถานที่เกิดเหตุ, .6fc36fC'béatrice' > 'boris'

มันไม่ใช่แค่สำเนียง มีกฎระเบียบที่ซับซ้อนมากขึ้นด้วยยัติภังค์เครื่องหมายวรรคตอนและตัวอักษรแปลก ๆ œเช่น กฎทางวัฒนธรรมที่แปลกประหลาดจะเกิดขึ้นในทุก ๆ ที่

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

ในกรณีCนี้เป็นตัวเลือกที่สมเหตุสมผลและมันมีข้อดีของการเป็นเร็วกว่าเพราะไม่มีอะไรสามารถเอาชนะการเปรียบเทียบไบต์ที่บริสุทธิ์

LC_CTYPE

การLC_CTYPEตั้งค่าเป็น 'C' หมายถึงฟังก์ชั่น C เช่นisupper(c)หรือtolower(c)ให้ผลลัพธ์ที่คาดหวังสำหรับตัวละครในช่วง US-ASCII เท่านั้น (นั่นคือมากถึง codepoint 0x7F ใน Unicode)

เพราะฟังก์ชั่น SQL ชอบupper(), lower()หรือinitcap จะดำเนินการใน Postgres ด้านบนของฟังก์ชั่น libc เหล่านี้พวกเขากำลังได้รับผลกระทบโดยเร็วที่สุดเท่าที่มีตัวละครที่ไม่ใช่สหรัฐ ASCII ในสตริง

ตัวอย่าง:

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)

สำหรับCโลแคลéจะถือว่าเป็นอักขระที่ไม่สามารถจัดแบ่งได้

ผลลัพธ์ที่ผิดในทำนองเดียวกันก็มีการแสดงผลปกติ

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

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

ดังนั้นถ้าฉันทำให้ถูกต้องเราจะมีปัญหาการสั่งซื้อแม้ว่าคุณจะทำเซิร์ฟเวอร์ UTF-8? ฉันเดาว่าการตั้งค่าระบบ LC_CTYPE บน UTF-8 หรือการรวบรวม PostgreSQL ใน UTF-8 จะส่งผลให้เกิดปัญหาการเปรียบเทียบแบบเดียวกันกับที่คุณชี้
Gregoire D.

หากต้องการขยายในเรื่องนี้จะเป็นไปได้ไหมที่จะบังคับให้เรียงเรียงตามแบบสอบถามเพื่อให้การเปรียบเทียบนั้นถูกต้อง
Gregoire D.

ใช่การเปรียบเทียบสตริง invidual สามารถฝังกฎการเรียงของตัวเองที่ฉันทำในคำตอบนี้ด้วยหลังจากที่collate "C" order byขึ้นอยู่กับคุณในการพิจารณาว่าแอปพลิเคชันของคุณต้องการที่ไหนและที่ไหน แอปพลิเคชันส่วนใหญ่ไม่สนใจ
Daniel Vérité

1
นอกจากนี้โปรดทราบว่าแต่ละคอลัมน์อาจมีตัวCOLLATEระบุที่แตกต่างจากฐานข้อมูล
Daniel Vérité

2
คำตอบนี้เป็นจริงสำหรับ LC_COLLATE ไม่ใช่ LC_CTYPE LC_CTYPE ใช้ในการตัดสินใจว่าตัวละครเป็นตัวเลขตัวอักษรช่องว่างเครื่องหมายวรรคตอนและอื่น ๆ
jjanes

10

อ้างอิงถึงคำตอบที่ยอมรับได้ของ Daniel เกี่ยวกับการเรียงลำดับโดยใช้การเรียงหน้าโปรดทราบว่าหากคุณใช้ PostgreSQL บน Mac การเปรียบเทียบที่คุณต้องการอาจไม่ทำงานตามที่คุณคาดหวังเนื่องจากการตั้งค่าไม่เพียงพอสำหรับการเปรียบเทียบบางระดับระบบปฏิบัติการ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับปัญหาได้ที่นี่:

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

นี่ไม่ใช่ปัญหาเฉพาะ PostgreSQL โดยเฉพาะ แต่เป็นปัญหากับการกำหนดค่าเริ่มต้นของ Mac สำหรับการตั้งค่าการเปรียบเทียบ ระบบปัจจุบันของฉันใช้ PostgreSQL 9.3 บน OS X El Capitan เวอร์ชัน 10.11 และได้รับผลกระทบจากปัญหานี้ ระบบของฉันส่งคืนผลลัพธ์แบบสอบถามเดียวกันโดยไม่คำนึงว่าฉันใช้การเรียงลำดับ“ fr_FR” หรือ“ en_US” ตัวอย่างเช่น:

ใช้การเรียงหน้า“ 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

ใช้การเรียงหน้า“ en_US”:

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

ในระบบของฉันการตั้งค่าการเรียง (ในระดับระบบปฏิบัติการ) จะเหมือนกันสำหรับ“ fr_FR” และ“ en_US” ดังที่แสดงในเชลล์โดยการใช้ diff:

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

หวังว่าข้อมูลเพิ่มเติมนี้จะเป็นประโยชน์สำหรับทุกคนที่อ่านข้อความนี้ที่ใช้ PostgreSQL บน Mac ที่มีปัญหาจากปัญหานี้


ฉันจะทำให้มันทำงานใน Macs ที่ทันสมัยได้อย่างไร คุณเคยผ่านอะไรมาบ้างเพื่อให้มันทำงานใน mac ของคุณหรือไม่?
Dinesh Kumar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.