PostgreSQL "aksan duyarlı olmayan" harmanlamaları destekliyor mu?


98

Microsoft SQL Server'da "aksan duyarsız" bir harmanlama belirtmek mümkündür (bir veritabanı, tablo veya sütun için), bu da aşağıdaki gibi bir sorgu için mümkün olduğu anlamına gelir

SELECT * FROM users WHERE name LIKE 'João'

Joaoadı olan bir satır bulmak için .

Unaccent_string katkı işlevini kullanarak PostgreSQL'deki dizelerden aksanları çıkarmanın mümkün olduğunu biliyorum , ancak PostgreSQL'in bu "aksan duyarsız" harmanlamaları destekleyip desteklemediğini merak ediyorum, böylece SELECTyukarıdakiler çalışır.


Akımsız bir FTS sözlüğü oluşturmak için bu cevaba bakın: stackoverflow.com/a/50595181/124486
Evan Carroll

Büyük / küçük harfe duyarlı mı yoksa büyük / küçük harfe duyarlı olmayan aramalar mı istiyorsunuz?
Evan Carroll

Yanıtlar:


206

Bunun için aksan olmayan modülü kullanın - bu, bağlantı kurduğunuzdan tamamen farklıdır.

unaccent, aksanları (aksan işaretleri) sözcükbirimlerinden kaldıran bir metin arama sözlüğüdür.

Aşağıdakilerle veritabanı başına bir kez yükleyin:

CREATE EXTENSION unaccent;

Aşağıdaki gibi bir hata alırsanız:

ERROR: could not open extension control file
"/usr/share/postgresql/<version>/extension/unaccent.control": No such file or directory

Katkı paketini, bu ilgili yanıtta belirtildiği gibi veritabanı sunucunuza yükleyin:

Diğer şeylerin yanı sıra, unaccent()örneğinizle kullanabileceğiniz işlevi sağlar ( LIKEgerekli görünmeyen yerlerde ).

SELECT *
FROM   users
WHERE  unaccent(name) = unaccent('João');

Dizin

Bu tür bir sorgu için bir dizin kullanmak üzere ifade üzerinde bir dizin oluşturun . Ancak , Postgres yalnızca IMMUTABLEdizinler için işlevleri kabul eder . Bir işlev aynı girdi için farklı bir sonuç döndürebiliyorsa, dizin sessizce kırılabilir.

unaccent()sadece STABLEdeğilIMMUTABLE

Ne yazık ki, unaccent()sadece STABLEöyle değil IMMUTABLE. Pgsql-bugs hakkındaki bu konuya göre , bunun üç sebebi var:

  1. Bir sözlüğün davranışına bağlıdır.
  2. Bu sözlüğe hiçbir fiziksel bağlantı yok.
  3. Bu nedenle search_path, kolaylıkla değişebilen akıma da bağlıdır .

Web'deki bazı öğreticiler , işlev değişkenliğini sadece IMMUTABLE. Bu kaba kuvvet yöntemi belirli koşullar altında kırılabilir.

Diğerleri basit bir IMMUTABLEsarmalayıcı işlevi önermektedir (geçmişte kendim yaptığım gibi).

Kullanılan sözlüğü açıkça bildiren iki parametre ile varyantın yapılıp yapılmayacağına dair süregelen bir tartışma var IMMUTABLE. Burayı veya buradan okuyun .

Diğer bir alternatif ise, Github'daunaccent() sağlanan Musicbrainz'in IMMUTABLE işlevine sahip bu modül olabilir . Kendim test etmedim. Sanırım daha iyi bir fikir buldum :

Şimdilik en iyisi

Bu yaklaşım, etrafta dolaşan diğer çözümler için daha verimli ve daha güvenli . İki parametreli formu fiziksel bağlantılı şema nitelikli işlev ve sözlükle çalıştıran
bir IMMUTABLESQL sarmalayıcı işlevi oluşturun .

Değişmez olmayan bir işlevi iç içe yerleştirmek, işlev satırını devre dışı bırakacağından, bunu C işlevinin (sahte) de beyan ettiği bir kopyasına dayandırın IMMUTABLE. Onun tek amacı, SQL fonksiyonu ambalajında kullanılacak. Kendi başına kullanılması amaçlanmamıştır.

C işlevinin bildiriminde sözlüğü sert bir şekilde bağlamanın bir yolu olmadığı için karmaşıklığa ihtiyaç vardır. (Misiniz C kodu kendisi kesmek gerekir.), SQL sarıcı fonksiyonu yok ve inlining her ikisinin de düzgün sağlar ve sentezleme endeksler.

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$;

Bırak PARALLEL SAFEPostgres 9.5 veya üstü için her iki işlevlerden.

publicuzantıyı yüklediğiniz şema olmak ( publicvarsayılandır).

Açık tür bildirimi ( regdictionary), kötü niyetli kullanıcılar tarafından işlevin aşırı yüklenmiş varyantlarıyla varsayımsal saldırılara karşı koruma sağlar.

Daha önce, temel bir sarmalayıcı işlev savunduğu STABLEfonksiyonu unaccent()unaccent modülü ile sevk edilir. Bu devre dışı bırakılan işlev satır içi . Bu sürüm, daha önce burada sahip olduğum basit sarmalayıcı işlevinden on kat daha hızlı çalışır.
Ve bu, SET search_path = public, pg_tempişleve eklenen ilk sürümden iki kat daha hızlıydı - sözlüğün de şema nitelikli olabileceğini keşfedene kadar. Yine de (Postgres 12) belgelerden çok açık değil.

Eğer bir: C işlevler oluşturmak için gerekli ayrıcalıkları yoksun, geri en iyi ikinci uygulanması için vardır IMMUTABLEetrafında işlev sarmalayıcı STABLE unaccent()modülü tarafından sağlanan işlevi:

CREATE OR REPLACE FUNCTION public.f_unaccent(text)
  RETURNS text AS
$func$
SELECT public.unaccent('public.unaccent', $1)  -- schema-qualify function and dictionary
$func$  LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT;

Son olarak, sorguları hızlı yapmak için ifade dizini :

CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));

Dizinleri yeniden oluşturmayan yerinde bir ana sürüm yükseltmesi gibi, işlev veya sözlükte yapılan herhangi bir değişiklikten sonra bu işlevi içeren dizinleri yeniden oluşturmayı unutmayın . Son ana sürümlerin hepsinde unaccentmodül için güncellemeler vardı .

Sorguları dizine uyacak şekilde uyarlayın (böylece sorgu planlayıcı onu kullanacaktır):

SELECT * FROM users
WHERE  f_unaccent(name) = f_unaccent('João');

Doğru ifadede işleve ihtiyacınız yok. Orada 'Joao'doğrudan gibi vurgusuz dizeler de sağlayabilirsiniz .

Daha hızlı işlev, ifade indeksini kullanarak çok daha hızlı sorgulara dönüşmez . Bu, önceden hesaplanmış değerlerle çalışır ve zaten çok hızlıdır. Ancak dizin bakımı ve dizini kullanmayan sorgular faydalıdır.

İstemci programları için güvenlik, Postgres 10.3 / 9.6.8 vb. İle sıkılaştırılmıştır . Herhangi bir dizinde kullanıldığında gösterildiği gibi, şema nitelikli işlevi ve sözlük adını belirlemeniz gerekir . Görmek:

Bitişik harfler

Postgres 9.5 veya 'older ' veya 'ß' gibi daha eski bitişik harflerin manuel olarak genişletilmesi gerekir (buna ihtiyacınız varsa), çünkü unaccent()her zaman tek bir harfin yerini alır:

SELECT unaccent('Œ Æ œ æ ß');

unaccent
----------
E A e a S

Postgres 9.6'da bu güncellemenin vurgulanmasını seveceksiniz :

Uzatın contrib/unaccent'in standart unaccent.rulesUnicode bilinen tüm aksan işaretleri işlemek için dosyayı ve doğru olarak bitişik harfler genişletmek (Thomas Munro Léonard Benedetti)

Cesur vurgu benim. Şimdi anlıyoruz:

SELECT unaccent('Œ Æ œ æ ß');

unaccent
----------
OE AE oe ae ss

Desen eşleştirme

İçin LIKEveya ILIKEkeyfi desenleri ile, modül ile birleştirmek pg_trgmPostgreSQL 9.1 veya daha sonra. Bir trigram GIN (tipik olarak tercih edilir) veya GIST ifade indeksi oluşturun. GIN için örnek:

CREATE INDEX users_unaccent_name_trgm_idx ON users
USING gin (f_unaccent(name) gin_trgm_ops);

Şunlar gibi sorgular için kullanılabilir:

SELECT * FROM users
WHERE  f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');

GIN ve GIST indekslerinin bakımı düz btree'den daha pahalıdır:

Sadece sola bağlantılı desenler için daha basit çözümler var. Kalıp eşleştirme ve performans hakkında daha fazla bilgi:

pg_trgmayrıca "benzerlik" ( %) ve "mesafe" ( <->) için yararlı operatörler sağlar .

Trigram indeksleri, ~et al. ve büyük / küçük harfe duyarsız kalıp eşleştirmesi ILIKE:


Çözümünüzde dizinler kullanılıyor mu yoksa üzerinde bir dizin oluşturmam gerekir unaccent(name)mi?
Daniel Serodio

@ErwinBrandstetter psql 9.1.4'te, unaccent işlevi INMUTABLE yerine STABLE olduğu için "indeks ifadesindeki fonksiyonlar IMMUTABLE olarak işaretlenmelidir" alıyorum. Ne önerirsiniz?
e3matheus

1
@ e3matheus: Sağladığım önceki çözümü test etmediğim için suçlu hissettim, cevabımı araştırdım ve şu ana kadar ortalıkta dolaşanlardan daha iyi (IMHO) bir çözümle yanıtımı güncelledim.
Erwin Brandstetter

Harmanlama utf8_general_cibu tür sorunların cevabı değil mi?
Orta

5
Cevaplarınız Postgres dokümantasyonu kadar iyidir: olağanüstü!
elektrotip

6

Hayır, PostgreSQL bu anlamda harmanlamaları desteklemez

PostgreSQL böyle harmanlamaları desteklemez (aksan duyarsız olsun ya da olmasın) çünkü nesneler ikili eşit olmadıkça hiçbir karşılaştırma eşit olarak dönemez. Bunun nedeni, dahili olarak hash indeksi gibi şeyler için çok fazla karmaşıklık getirmesidir. Bu nedenle, en katı anlamıyla harmanlamalar eşitliği değil, yalnızca düzeni etkiler .

Çözümler

Lexemes Unaccents Tam Metin Arama Sözlük.

FTS için, kullanarak kendi sözlüğünüzü tanımlayabilirsiniz unaccent,

CREATE EXTENSION unaccent;

CREATE TEXT SEARCH CONFIGURATION mydict ( COPY = simple );
ALTER TEXT SEARCH CONFIGURATION mydict
  ALTER MAPPING FOR hword, hword_part, word
  WITH unaccent, simple;

Daha sonra işlevsel bir indeksle indeksleyebilirsiniz,

-- Just some sample data...
CREATE TABLE myTable ( myCol )
  AS VALUES ('fóó bar baz'),('qux quz');

-- No index required, but feel free to create one
CREATE INDEX ON myTable
  USING GIST (to_tsvector('mydict', myCol));

Artık çok basit bir şekilde sorgulayabilirsiniz

SELECT *
FROM myTable
WHERE to_tsvector('mydict', myCol) @@ 'foo & bar'

    mycol    
-------------
 fóó bar baz
(1 row)

Ayrıca bakınız

Kendi kendine akmayan.

unaccentModül ayrıca Çek şu kontrol için, FTS-entegrasyon olmadan tek başına kullanılabilir Erwin'ın cevap


2

PostgreSQL'in harmanlama için temeldeki işletim sistemine dayandığından oldukça eminim. Bu mu destekleyen yeni alfabe oluşturma ve alfabe özelleştirme . Senin için ne kadar iş olacağından emin değilim. (Oldukça fazla olabilir.)


1
Yeni harmanlama desteği şu anda temelde işletim sistemi yerel ayarları için sarmalayıcılar ve takma adlarla sınırlıdır. Bu çok basit. Filtre işlevleri, özel karşılaştırıcılar veya gerçek özel harmanlamalar için ihtiyacınız olan hiçbiri için destek yoktur.
Craig Ringer
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.