Dizilerin verimli birleştirilmesi (yinelenenlerin kaldırılması)


10

İki masam var left2ve right2. Her iki tablo da büyük olacaktır (1-10M satırlar).

CREATE TABLE left2(id INTEGER, t1 INTEGER, d INTEGER);
ALTER TABLE left2 ADD PRIMARY KEY (id,t1);

CREATE TABLE right2( t1 INTEGER, d INTEGER, arr INTEGER[] );
ALTER TABLE right2 ADD PRIMARY KEY(t1,d);

Bu tür bir sorgu gerçekleştireceğim:

SELECT l.d + r.d,
       UNIQ(SORT((array_agg_mult(r.arr)))
FROM left2 l,
     right2 r
WHERE l.t1 = r.t1
GROUP BY l.d + r.d
ORDER BY l.d + r.d;

Dizilerin toplanması için bu işlevi kullanıyorum:

CREATE AGGREGATE array_agg_mult(anyarray) (
SFUNC=array_cat,
STYPE=anyarray,
INITCOND='{}');

Dizileri birleştirdikten sonra modülün UNIQişlevini kullanıyorum intarray. Bunu yapmanın daha etkili bir yolu var mı? arrBirleştirmeyi hızlandırmak için sahada herhangi bir dizin var mı (yinelenenleri kaldırarak)? Toplama işlevi kopyaları doğrudan kaldırabilir mi? Orijinal diziler yardımcı olursa sıralanmış (ve benzersizdir) olarak değerlendirilebilir.

SQL Fiddle burada :


Milyonlarca satırı bir kerede sorgulayacak mısınız? Sonuçla ne yapıyorsun? Yoksa birkaçını seçmek için tahminler olacak mı? right2.arr Demo şemanızın önerdiği gibi NULL olabilir mi? Sonuç olarak sıralı dizilere mi ihtiyacınız var?
Erwin Brandstetter

Yanıtlar:


9

Doğru sonuçlar?

İlk önce: doğruluk. Bir dizi benzersiz eleman mı üretmek istiyorsunuz? Mevcut sorgunuz bunu yapmıyor. Fonksiyon uniq()gelen intarray modülüne sadece vaat:

bitişik kopyaları kaldır

Gibi Kılavuzda , ihtiyacınız olacaktır:

SELECT l.d + r.d, uniq(sort(array_agg_mult(r.arr)))
FROM   ...

Ayrıca sıralı diziler verir - bunu istediğinizi varsayarsak, netleştirmediniz.

Görmeni var sort() senin keman içinde bu sadece sorunuzun bir yazım hatası olabilir, böylece.

Postgres 9.5

Her iki durumda da , yeni Postgres 9.5'i (şu anda beta) seveceksiniz . Bu yeteneklerini sağlar array_agg_mult()çok daha hızlı kutunun çıkışı ve:

Dizi işleme için başka performans geliştirmeleri de yapılmıştır.

Sorgu

Temel amacı array_agg_mult()çok boyutlu dizileri birleştirmektir, ancak yine de yalnızca 1 boyutlu diziler üretiyorsunuz. Yani en azından bu alternatif sorguyu denemek istiyorum:

SELECT l.d + r.d AS d_sum, array_agg(DISTINCT elem) AS result_arr
FROM   left2  l
JOIN   right2 r USING (t1)
     , unnest(r.arr) elem
GROUP  BY 1
ORDER  BY 1;

Ayrıca sorunuzu da yanıtlar:

Toplama işlevi kopyaları doğrudan kaldırabilir mi?

Evet, yapabilir DISTINCT. Ancak bu uniq(), tam sayı dizileri için optimize edilmiş tam sayı dizilerinden daha hızlı değildir , ancak DISTINCTtüm niteleyici veri türleri için geneldir.

intarrayModül gerektirmez . Ancak , sonuç mutlaka sıralanmaz. Postgres, DISTINCT(IIRC) için değişken algoritmalar kullanır , büyük kümeler genellikle karmadır, sonra açık eklemediğiniz sürece sonuç sıralanmaz ORDER BY. Eğer sıralı diziler gerekiyorsa, olabilir eklemek ORDER BYdoğrudan agrega işlevine:

array_agg(DISTINCT elem ORDER BY elem)

Ancak bu genellikle önceden sıralanmış verileri beslemekten daha yavaştırarray_agg() (bir çok çeşit ve birçok küçük çeşit). Bu yüzden bir alt sorgu sıralamak ve daha sonra toplamak:

SELECT d_sum, uniq(array_agg(elem)) AS result_arr
FROM  (
   SELECT l.d + r.d AS d_sum, elem
   FROM   left2  l
   JOIN   right2 r USING (t1)
        , unnest(r.arr) elem
   ORDER  BY 1, 2
   ) sub
GROUP  BY 1
ORDER  BY 1;

Postgres 9.4'teki üstünkörü testimdeki en hızlı varyanttı.

Sağladığınız SQL Fiddle .

indeks

Burada herhangi bir dizin için fazla potansiyel görmüyorum. Tek seçenek:

CREATE INDEX ON right2 (t1, arr);

Yalnızca bununla ilgili yalnızca dizin taramaları alırsanız mantıklıdır - temel tablo right2yalnızca bu iki sütundan daha genişse ve kurulumunuz yalnızca dizin taramaları için uygunsa gerçekleşir . Ayrıntılar Postgres Wiki'deki.


Teşekkürler +1. Yine de daha sonra UNNEST yapmak zorunda kalacağım, ancak dizilerdeki yinelenenleri kaldırmanın daha hızlı olup olmadığını kontrol etmek istiyorum.
Alexandros

0

Gerçekten hayal kırıklığına uğradım, bu Microsoft Access'te yapmak kolay bir şey. Bir "yinelenenleri kaldır" sorgusu oluşturabilir ve nasıl çalıştığını görmek için SQL'e bakabilirsiniz. Bakmak için bir Windows makinesini ateşlemem gerekecek. Değişir, sorgu sihirbazı yapar.

Bence işe yarar bir şey tüm verilerinizi tek bir tabloya yüklemek ve sonra yeni bir tabloya DISTINCT SELECT yapmak olduğunu düşünüyorum. Ayrıca, sıradayken yan tümce ile bir sırayla yapıştırabilirsiniz. Bir şekilde bir yıl önce yaptım, öyle olmalı.

2 yıllık sıcaklık verilerini birleştiriyorum, sensör her dakika aynı veri noktasının 2 kopyasını yedek bir koruma olarak gönderir. Bazen biri çöpe atılır, ama sadece birini tutmak istiyorum. Ayrıca dosyalar arasında çakışma var.

Veriler tüm çalışma boyunca tam olarak aynı formattaysa, bir unix makinesinde şöyle bir şey yapabilirsiniz

cat *.tab > points.txt
sort -n < points.txt > sorted.txt
uniq -u sorted.txt unique.txt

Ancak uniq, satırları dize olarak karşılaştırır ve örneğin 18.7000, 18.7 ile aynı değildir. Yazılımımı 2 yıl boyunca değiştirdim, böylece her iki format da var.


Postgres hayal kırıklığına uğramış mı? Access dizileri bile var mı?
ypercubeᵀᴹ

Bilmiyorum ama kopyaları kaldırabilir, veri temizliğinde yeterince yaygın bir sorundur. Farklı seçin yeterince yakın. Gerçek dünyadan ham verileriniz üzerinde her zaman kontrol sahibi olmazsınız.
Alan Corey
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.