PostgreSQL dizin dizisi sütunları olabilir mi?


144

Belgelerde bu soruya kesin bir cevap bulamıyorum. Sütun bir dizi türüyse, girilen tüm değerler ayrı ayrı dizine eklenir mi?

Bir int[]sütun ile basit bir tablo oluşturdum ve üzerine benzersiz bir dizin koydum. Ben indeks dizi öğelerin bir bileşik değil, her öğenin bir dizin olduğuna inanıyorum yol açar ints aynı dizi ekleyemedi fark ettim.

INSERT INTO "Test"."Test" VALUES ('{10, 15, 20}');
INSERT INTO "Test"."Test" VALUES ('{10, 20, 30}');

SELECT * FROM "Test"."Test" WHERE 20 = ANY ("Column1");

Dizin bu sorguya yardımcı oluyor mu?


Yanıtlar:


181

Evet, bir dizini dizinleyebilirsiniz, ancak dizi işleçlerini ve GIN dizin türünü kullanmanız gerekir .

Misal:

    CREATE TABLE "Test"("Column1" int[]);
    INSERT INTO "Test" VALUES ('{10, 15, 20}');
    INSERT INTO "Test" VALUES ('{10, 20, 30}');

    CREATE INDEX idx_test on "Test" USING GIN ("Column1");

    -- To enforce index usage because we have only 2 records for this test... 
    SET enable_seqscan TO off;

    EXPLAIN ANALYZE
    SELECT * FROM "Test" WHERE "Column1" @> ARRAY[20];

Sonuç:

Bitmap Heap Scan on "Test"  (cost=4.26..8.27 rows=1 width=32) (actual time=0.014..0.015 rows=2 loops=1)
  Recheck Cond: ("Column1" @> '{20}'::integer[])
  ->  Bitmap Index Scan on idx_test  (cost=0.00..4.26 rows=1 width=0) (actual time=0.009..0.009 rows=2 loops=1)
        Index Cond: ("Column1" @> '{20}'::integer[])
Total runtime: 0.062 ms
Not

birçok durumda gin__int_ops seçeneğinin gerekli olduğu anlaşılıyor

create index <index_name> on <table_name> using GIN (<column> gin__int_ops)

Henüz gin__int_ops seçenekleri olmadan && ve @> operatörü ile çalışacağı bir durum görmedim


19
OP'nin tahmin ettiği gibi, bu aslında tek tek dizi değerlerini endekslemez, bunun yerine tüm diziyi endeksler. Bu, söz konusu sorguya yardımcı olmasına rağmen (açıklama planına bakın), bu, bireysel dizi değerleri üzerinde benzersiz kısıtlamalar (kolayca) oluşturamayacağınız anlamına gelir. Bununla birlikte, tamsayı dizileri kullanıyorsanız, birçok durumda daha hızlı olabilen tek tek dizi değerlerini endekslemek için katkıda bulunan "intarray" modülünü kullanabilirsiniz. (IIRC, metin değerleri için bazı çalışmalar yapılıyor, ancak katkıda bulunanlar muhtemelen bitirmeye yardımcı olabilirler).
xzilla

15
Lütfen kod örneklerinde PostgreSQL tanımlayıcılarında büyük harf kullanmayın, sadece alıntı / vaka katlama kurallarına aşina olmayan insanları, özellikle PostgreSQL'de yeni olanları karıştırır.
intgr

6
Yorumumu burada tekrarlamak için: deneyimlerimden, bu dizinler sütunlar için kullanılmadığı sürece gin__int_ops çok az veya hiç hızlanma sunmaz integer[]. Bu op sınıfını bulana kadar yıllarca hayal kırıklığı ve başka çözümler aramam gerekiyordu. Sınırda bir mucize işçi.
IamIC

1
@IamIC bu bir dizeler dizine endeksleme rahatsız olmamalı anlamına mı geliyor? Ve sadece tamsayı dizilerini indekslemeliyim?
ryan2johnson9

93

@Tregoreg , sunduğu ödülün yorumunda bir soru sordu :

Mevcut cevapları işe yaramadı. Dizi tipi sütunda GIN dizini kullanmak, ANY () operatörünün performansını artırmaz. Gerçekten bir çözüm yok mu?

@ Frank'in kabul ettiği cevap , Postgres 11 için hala doğru olan dizi operatörlerini kullanmanızı söyler . Kılavuz:

... PostgreSQL'in standart dağıtımı, diziler için şu işleçleri kullanarak dizinlenmiş sorguları destekleyen bir GIN işleç sınıfı içerir:

<@
@>
=
&&

Standart dağıtımdaki GIN dizinleri için yerleşik operatör sınıflarının tam listesi burada.

Postgres'de dizinler , yalnızca veri türlerine veya işlevlere veya başka bir şeye değil, operatörlere (belirli türler için uygulanır) bağlıdır. Bu Postgres'in orijinal Berkeley tasarımından bir miras ve şimdi değiştirmek çok zor. Ve genellikle iyi çalışıyor. İşte Tom Lane bu konuda yorum yapan pgsql-bugs hakkında bir konu.

Bazı PostGis işlevleri (gibi ST_DWithin()) bu prensibi ihlal ediyor gibi görünüyor, ama öyle değil. Bu işlevler, ilgili işleçleri kullanmak için dahili olarak yeniden yazılır .

Dizine alınan ifade , operatörün solunda olmalıdır . Çoğu işleç için ( yukarıdakilerin tümü dahil ) sorgu planlayıcısı, dizinli ifadeyi sağa yerleştirirseniz işlenenleri çevirerek bunu başarabilir - a'nın COMMUTATORtanımlandığı göz önüne alınırsa . ANYYapı çeşitli operatörler ile kombinasyon halinde kullanılan bir operatör kendisi değil edilebilir. Dizi öğelerindeconstant = ANY (array_expression) yalnızca =operatörü destekleyen dizinler kullanıldığında hak kazanırız ve bunun için bir komütatöre ihtiyacımız olur . GIN dizinleri çıktı.= ANY()

Postgres şu anda ondan bir GIN ile endekslenebilir ifade türetecek kadar akıllı değildir. Yeni başlayanlar için, constant = ANY (array_expression)olduğu tamamen eşdeğer değildir için array_expression @> ARRAY[constant]. Dizi işleçleri herhangi bir NULL öğesi varsa hata döndürür ; ANYyapı ise her iki tarafta da NULL ile başa çıkabilir. Ve veri türü uyuşmazlıkları için farklı sonuçlar vardır.

İlgili cevaplar:

asides

Değerleri olmayan integerdizilerle (( ya da int4değil ) dizilerle çalışırken ( örneğinizin ima ettiği gibi) özel, daha hızlı işleçler ve dizin desteği sağlayan ek modülü göz önünde bulundurun . Görmek:int2int8NULLintarray

UNIQUESorunuzda yanıtsız bırakılan kısıtlamaya gelince : Bu, tüm dizi değeri (şüphelendiğiniz gibi) üzerinde bir btree dizini ile uygulanır ve hiç öğe aramasına yardımcı olmaz . Detaylar:


1
Aaaaaaah, şu anda oldukça utanmış hissediyordum, ancak teorik olarak mümkün olsa bile postgres'in endeksi kullanmayacağı aklıma gelmedi. Belki de bunun nedeni, endekslerin operatörlere bağlı olması gibi postgres hakkındaki içgörü eksikliğimdir. Kötü sorularıma cevap vermek ve bilginizi paylaşmak için zaman ayırdığınız için teşekkür ederiz!
15'de Tregoreg

6
@Tregoreg: Çok utanmayın, gerçekten çok açık değil. İlk karşılaştığımda bununla kafam karıştığını hatırlıyorum. Eklenen soru ve açıklama genel halk için oldukça yararlı olmalıdır.
Erwin Brandstetter

1
Deneyimlerime göre, bu dizinler sütunlar için kullanılmadığı sürece gin__int_ops çok az veya hiç hızlanma sunmaz integer[]. Bu op sınıfını bulana kadar yıllarca hayal kırıklığı ve başka çözümler aramam gerekiyordu. Sınırda bir mucize işçi.
IamIC

2
@IamIC: İntarray'a işaretçiler ekledim. Belirttiğiniz gibi dikkate değer görünüyor.
Erwin Brandstetter

İçin ANY (array_expression) = constantifadeleri, CİN indeksleri cezası çalışır?
user10375

37

Artık münferit dizi elemanlarını endekslemek mümkün. Örneğin:

CREATE TABLE test (foo int[]);
INSERT INTO test VALUES ('{1,2,3}');
INSERT INTO test VALUES ('{4,5,6}');
CREATE INDEX test_index on test ((foo[1]));
SET enable_seqscan TO off;

EXPLAIN ANALYZE SELECT * from test WHERE foo[1]=1;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Scan using test_index on test  (cost=0.00..8.27 rows=1 width=32) (actual   time=0.070..0.071 rows=1 loops=1)
   Index Cond: (foo[1] = 1)
 Total runtime: 0.112 ms
(3 rows)

Bu en azından Postgres 9.2.1 üzerinde çalışır. Her dizi dizini için ayrı bir dizin oluşturmak gerektiğini unutmayın, benim örnekte sadece ilk öğeyi dizine.


28
Kaybolmasın - bu yaklaşım, ANY () operatörünü kullanmak istediğiniz değişken uzunluk dizisi için umutsuzdur.
καrτhικ

24
Bu gerçekten çok kullanışlı değil. Sabit sayıda dizi öğeniz varsa, her dizi öğesi için daha pahalı bir ifade dizini oluşturmak yerine her öğe (ve düz btree dizinleri) için ayrı sütunlar kullanmayı tercih edersiniz. Tek tek sütunların depolanması, dizi yükü olmadan da çok daha ucuzdur.
Erwin Brandstetter
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.