ใช้โมดูล unaccentสำหรับสิ่งนั้นซึ่งแตกต่างอย่างสิ้นเชิงกับสิ่งที่คุณกำลังเชื่อมโยง
unaccent คือพจนานุกรมค้นหาข้อความที่ลบสำเนียง (เครื่องหมายกำกับเสียง) ออกจากตัวอักษร
ติดตั้งหนึ่งครั้งต่อฐานข้อมูลด้วย:
CREATE EXTENSION unaccent;
หากคุณได้รับข้อผิดพลาดเช่น:
ERROR: could not open extension control file
"/usr/share/postgresql/<version>/extension/unaccent.control": No such file or directory
ติดตั้งแพ็คเกจ Contrib บนเซิร์ฟเวอร์ฐานข้อมูลของคุณตามที่ได้รับคำแนะนำในคำตอบที่เกี่ยวข้องนี้:
เหนือสิ่งอื่นใดมันมีฟังก์ชันที่unaccent()
คุณสามารถใช้ได้กับตัวอย่างของคุณ (ซึ่งLIKE
ดูเหมือนว่าไม่จำเป็น)
SELECT *
FROM users
WHERE unaccent(name) = unaccent('João');
ดัชนี
การใช้ดัชนีสำหรับชนิดของแบบสอบถามที่สร้างดัชนีในการแสดงออก อย่างไรก็ตาม Postgres ยอมรับIMMUTABLE
ฟังก์ชันสำหรับดัชนีเท่านั้น หากฟังก์ชันสามารถส่งคืนผลลัพธ์ที่แตกต่างกันสำหรับอินพุตเดียวกันดัชนีอาจแตกอย่างเงียบ ๆ
unaccent()
STABLE
ไม่เท่านั้นIMMUTABLE
แต่น่าเสียดายที่unaccent()
เป็นเพียงไม่STABLE
IMMUTABLE
ตามหัวข้อนี้เกี่ยวกับ pgsql-bugsนี่เป็นเพราะสาเหตุสามประการ:
- ขึ้นอยู่กับลักษณะการทำงานของพจนานุกรม
- ไม่มีการเชื่อมต่อแบบใช้สายกับพจนานุกรมนี้
- ดังนั้นจึงขึ้นอยู่กับกระแส
search_path
ซึ่งสามารถเปลี่ยนแปลงได้ง่าย
บทเรียนบางอย่างIMMUTABLE
เกี่ยวกับการสั่งเว็บเพียงแค่เปลี่ยนความผันผวนของฟังก์ชั่น วิธีการบังคับแบบเดรัจฉานนี้สามารถทำลายได้ภายใต้เงื่อนไขบางประการ
คนอื่น ๆ แนะนำฟังก์ชั่นเสื้อคลุมแบบธรรมดาIMMUTABLE
(เหมือนที่ฉันทำเองในอดีต)
มีการถกเถียงกันอย่างต่อเนื่องว่าจะสร้างตัวแปรด้วยพารามิเตอร์สองตัว IMMUTABLE
ที่ประกาศพจนานุกรมที่ใช้อย่างชัดเจนหรือไม่ อ่านที่นี่หรือที่นี่
อีกหนึ่งทางเลือกที่จะเป็นโมดูลนี้ด้วยการเปลี่ยนรูปunaccent()
ฟังก์ชั่นโดย Musicbrainzให้บน Github ยังไม่ได้ทดสอบด้วยตัวเอง. ฉันคิดว่าฉันมีความคิดที่ดีกว่านี้ :
ดีที่สุดสำหรับตอนนี้
วิธีนี้เป็นวิธีที่มีประสิทธิภาพมากขึ้นเป็นโซลูชั่นอื่น ๆ ที่ลอยอยู่รอบ ๆ และปลอดภัยมากขึ้น
สร้างIMMUTABLE
ฟังก์ชัน SQL wrapper ที่เรียกใช้รูปแบบสองพารามิเตอร์ด้วยฟังก์ชันและพจนานุกรมที่มีคุณสมบัติสคีมาแบบใช้สายยาก
เนื่องจากการซ้อนฟังก์ชันที่ไม่เปลี่ยนรูปจะปิดการใช้งานฟังก์ชันอินไลน์อิงตามสำเนาของฟังก์ชัน C (ปลอม) ที่ประกาศIMMUTABLE
ด้วยเช่นกัน ใช้เพียงวัตถุประสงค์เพื่อนำมาใช้ในฟังก์ชั่นเสื้อคลุม SQL ไม่ได้มีไว้สำหรับใช้เอง
จำเป็นต้องมีความซับซ้อนเนื่องจากไม่มีวิธีการต่อสายพจนานุกรมในการประกาศฟังก์ชัน C (จำเป็นต้องแฮ็กโค้ด C เอง) ฟังก์ชัน SQL wrapper จะทำเช่นนั้นและอนุญาตให้ทั้งฟังก์ชันอินไลน์และดัชนีนิพจน์
CREATE OR REPLACE FUNCTION public.immutable_unaccent(regdictionary, text)
RETURNS text LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS
'$libdir/unaccent', 'unaccent_dict';
CREATE OR REPLACE FUNCTION public.f_unaccent(text)
RETURNS text LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
$func$
SELECT public.immutable_unaccent(regdictionary 'public.unaccent', $1)
$func$;
ดร็อปPARALLEL SAFE
จากทั้งสองฟังก์ชันสำหรับ Postgres 9.5 ขึ้นไป
public
เป็นสคีมาที่คุณติดตั้งส่วนขยาย ( public
เป็นค่าเริ่มต้น)
การประกาศประเภทอย่างชัดเจน ( regdictionary
) ป้องกันการโจมตีสมมุติด้วยฟังก์ชันที่มีรูปแบบมากเกินไปโดยผู้ใช้ที่เป็นอันตราย
ก่อนหน้านี้ฉันสนับสนุนฟังก์ชัน wrapper ตามSTABLE
ฟังก์ชันที่unaccent()
มาพร้อมกับโมดูล unaccent ที่ปิดการใช้งานอินไลน์ฟังก์ชั่น เวอร์ชันนี้ทำงานได้เร็วกว่าฟังก์ชัน Wrapper แบบธรรมดาถึง 10 เท่าที่ฉันมีก่อนหน้านี้
และนั่นเร็วกว่าเวอร์ชันแรกถึงสองเท่าซึ่งเพิ่มลงSET search_path = public, pg_temp
ในฟังก์ชันแล้ว - จนกระทั่งฉันค้นพบว่าพจนานุกรมนั้นสามารถเข้าเกณฑ์สคีมาได้เช่นกัน ยังคง (Postgres 12) ไม่ชัดเจนเกินไปจากเอกสาร
หากคุณไม่มีสิทธิ์ที่จำเป็นในการสร้างฟังก์ชัน C คุณจะกลับสู่การใช้งานที่ดีที่สุดอันดับสอง: IMMUTABLE
ฟังก์ชัน wrapper รอบ ๆSTABLE
unaccent()
ฟังก์ชันที่จัดเตรียมโดยโมดูล:
CREATE OR REPLACE FUNCTION public.f_unaccent(text)
RETURNS text AS
$func$
SELECT public.unaccent('public.unaccent', $1)
$func$ LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT;
ในที่สุดดัชนีนิพจน์เพื่อให้การสืบค้นรวดเร็ว :
CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));
อย่าลืมสร้างดัชนีที่เกี่ยวข้องกับฟังก์ชันนี้ใหม่หลังจากการเปลี่ยนแปลงฟังก์ชันหรือพจนานุกรมเช่นการอัปเกรดรุ่นหลักแบบแทนที่ซึ่งจะไม่สร้างดัชนีขึ้นมาใหม่ รุ่นใหญ่ล่าสุดทั้งหมดมีการอัปเดตสำหรับunaccent
โมดูล
ปรับการสืบค้นให้ตรงกับดัชนี (เพื่อให้ผู้วางแผนการสืบค้นใช้):
SELECT * FROM users
WHERE f_unaccent(name) = f_unaccent('João');
คุณไม่ต้องการฟังก์ชันในนิพจน์ที่ถูกต้อง นอกจากนี้คุณยังสามารถจัดหาสตริงที่ไม่เน้นเสียง'Joao'
ได้โดยตรง
ฟังก์ชั่นได้เร็วขึ้นไม่ได้แปลไปได้เร็วขึ้นมากแบบสอบถามโดยใช้ดัชนีการแสดงออก ซึ่งทำงานบนค่าที่คำนวณล่วงหน้าและรวดเร็วมากอยู่แล้ว แต่การบำรุงรักษาดัชนีและแบบสอบถามไม่ใช้ประโยชน์ของดัชนี
การรักษาความปลอดภัยสำหรับโปรแกรมไคลเอ็นต์ได้รับการรัดกุมด้วย Postgres 10.3 / 9.6.8 เป็นต้นคุณจำเป็นต้องกำหนดฟังก์ชันที่มีคุณสมบัติสคีมาและชื่อพจนานุกรมตามที่แสดงเมื่อใช้ในดัชนีใด ๆ ดู:
ลิเก
ในอักษรย่อPostgres 9.5 หรือเก่ากว่าเช่น 'Œ' หรือ 'ß' จะต้องขยายด้วยตนเอง (ถ้าคุณต้องการ) เนื่องจากunaccent()
แทนที่ตัวอักษรตัวเดียวเสมอ:
SELECT unaccent('Œ Æ œ æ ß');
unaccent
E A e a S
คุณจะหลงรักการอัปเดตนี้ถึงไม่มีใครสนใจใน Postgres 9.6 :
ขยายไฟล์contrib/unaccent
มาตรฐานunaccent.rules
เพื่อจัดการตัวกำกับเสียงทั้งหมดที่ Unicode รู้จักและขยายตัวอักษรอย่างถูกต้อง (Thomas Munro, Léonard Benedetti)
ฉันเน้นตัวหนา ตอนนี้เราได้รับ:
SELECT unaccent('Œ Æ œ æ ß');
unaccent
OE AE oe ae ss
การจับคู่รูปแบบ
สำหรับLIKE
หรือILIKE
กับรูปแบบที่กำหนดเองให้รวมเข้ากับโมดูลpg_trgm
ใน PostgreSQL 9.1 หรือใหม่กว่า สร้าง Trigram GIN (โดยทั่วไปแล้วจะดีกว่า) หรือดัชนีนิพจน์ GIST ตัวอย่างสำหรับ GIN:
CREATE INDEX users_unaccent_name_trgm_idx ON users
USING gin (f_unaccent(name) gin_trgm_ops);
สามารถใช้สำหรับการค้นหาเช่น:
SELECT * FROM users
WHERE f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');
ดัชนี GIN และ GIST มีราคาแพงกว่าในการดูแลรักษามากกว่า btree ธรรมดา:
มีวิธีแก้ปัญหาที่ง่ายกว่าสำหรับรูปแบบที่ยึดด้านซ้ายเท่านั้น ข้อมูลเพิ่มเติมเกี่ยวกับการจับคู่รูปแบบและประสิทธิภาพ:
pg_trgm
นอกจากนี้ยังมีประโยชน์ผู้ประกอบการสำหรับ "คล้ายคลึงกัน" ( %
) และ "ระยะทาง" (<->
)
ดัชนี Trigram ยังรองรับนิพจน์ทั่วไปอย่างง่ายด้วย~
et al และรูปแบบที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่จับคู่กับILIKE
: