Array_agg () neden birleşik olmayan ARRAY () yapıcısından daha yavaş?


14

Sadece 8.4 öncesi PostgreSQL için yazılmış bazı eski kodları inceliyordum ve gerçekten şık bir şey gördüm. Gün içinde özel bir işleve sahip olduğumu hatırlıyorum ama önceden array_agg()neye benzediğini unuttum . İnceleme için modern toplama bu şekilde yazılmıştır.

SELECT array_agg(x ORDER BY x DESC) FROM foobar;

Ancak, bir zamanlar böyle yazılmıştır,

SELECT ARRAY(SELECT x FROM foobar ORDER BY x DESC);

Bazı test verileriyle denedim.

CREATE TEMP TABLE foobar AS
SELECT * FROM generate_series(1,1e7)
  AS t(x);

Sonuçlar şaşırtıcıydı. #OldSchoolCool yolu çok daha hızlıydı:% 25 hızlanma. Dahası, SİPARİŞ olmadan basitleştirmek , aynı yavaşlığı gösterdi.

# EXPLAIN ANALYZE SELECT ARRAY(SELECT x FROM foobar);
                                                         QUERY PLAN                                                          
-----------------------------------------------------------------------------------------------------------------------------
 Result  (cost=104425.28..104425.29 rows=1 width=0) (actual time=1665.948..1665.949 rows=1 loops=1)
   InitPlan 1 (returns $0)
     ->  Seq Scan on foobar  (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.032..716.793 rows=10000000 loops=1)
 Planning time: 0.068 ms
 Execution time: 1671.482 ms
(5 rows)

test=# EXPLAIN ANALYZE SELECT array_agg(x) FROM foobar;
                                                        QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=119469.60..119469.61 rows=1 width=32) (actual time=2155.154..2155.154 rows=1 loops=1)
   ->  Seq Scan on foobar  (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.031..717.831 rows=10000000 loops=1)
 Planning time: 0.054 ms
 Execution time: 2174.753 ms
(4 rows)

Peki, burada neler oluyor. Neden bir dahili işlev olan array_agg , planlayıcının SQL voodoo'sundan çok daha yavaş?

" Gcc tarafından derlenen x86_64-pc-linux-gnu üzerinde PostgreSQL 9.5.5 (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005, 64-bit"

Yanıtlar:


17

Bir ARRAY yapıcısı hakkında "eski okul" veya "modası geçmiş" hiçbir şey yoktur (İşte ARRAY(SELECT x FROM foobar)budur). Her zamanki gibi modern. Basit dizi toplama için kullanın.

Kullanım kılavuzu:

Bir alt sorgunun sonuçlarından bir dizi oluşturmak da mümkündür. Bu formda, dizi yapıcısı anahtar sözcük ve ARRAYardından parantez içinde (parantezsiz) bir alt sorgu ile yazılır .

Toplama işleviarray_agg() , bir entegre edilebilir ki yönlü daha SELECTfazla sütun, aynı muhtemelen daha toplamalarla listesi SELECT, ile oluşturulabilir rasgele gruplara GROUP BY. ARRAY yapıcısı, SELECTtek bir sütunun döndürülmesinden yalnızca tek bir dizi döndürebilir .

Kaynak kodunu incelemedim, ancak çok daha çok yönlü bir aracın da daha pahalı olduğu açıkça görülüyor.

Dikkate değer bir fark: {}hiçbir satır uygun değilse ARRAY yapıcısı boş bir dizi ( ) döndürür . aynı array_agg()döndürür NULL.


7

Erwin tarafından kabul edilen cevabın aşağıdakilerle eklenebileceğine inanıyorum.

Genellikle, asıl sorudaki gibi geçici tablolar (indeksler olmadan) yerine endeksleri olan normal tablolarla çalışıyoruz. Toplama ARRAY_AGGsırasında sıralama yapıldığında , gibi toplamaların mevcut indekslerden yararlanamayacağını belirtmek yararlıdır .

Örneğin, aşağıdaki sorguyu varsayalım:

SELECT ARRAY(SELECT c FROM t ORDER BY id)

Bir dizinimiz varsa t(id, ...), dizin sıralı bir tarama ve tardından bir sıralama lehine kullanılabilir t.id. Ayrıca, dizide (burada c) sarılmakta olan çıktı sütunu, dizinin bir parçasıysa (dizin üzerinde t(id, c)veya içerme dizininde gibi t(id) include(c)), bu yalnızca dizin taraması bile olabilir.

Şimdi bu sorguyu aşağıdaki gibi yeniden yazalım:

SELECT ARRAY_AGG(c ORDER BY id) FROM t

Şimdi, toplama dizini kullanmaz ve bellekteki satırları sıralamak zorundadır (veya diskteki büyük veri kümeleri için daha da kötüsü). Bu her zaman ardışık bir tarama ve tardından toplama + sıralama olacaktır .

Bildiğim kadarıyla, bu resmi belgelerde belgelenmemiştir, ancak kaynaktan elde edilebilir. Bu mevcut tüm sürümler için geçerli olmalıdır, v11 dahil.


2
İyi bir nokta. Ancak bütün adaletlerde, array_agg()toplu işlevleri olan veya benzer toplama işlevlerine sahip sorgular , hala aşağıdaki gibi bir alt sorgu ile dizinlerden yararlanabilir SELECT ARRAY_AGG(c) FROM (SELECT c FROM t ORDER BY id) sub. Toplama başına ORDER BYcümle, örneğinizde dizin kullanımını engeller. Bir dizi yapıcısı olan hızlı daha array_agg()ya aynı indeksi (veya hiçbiri) ne zaman kullanabileceğini. Çok yönlü değil. Bakınız: dba.stackexchange.com/a/213724/3684
Erwin Brandstetter

1
Doğru, bu önemli bir ayrım. Bu yorumun sadece toplama işlevinin sıralanması gerektiğinde geçerli olduğunu açıkça belirtmek için cevabımı biraz değiştirdim. Gerçekten de basit durumda endeksten kar elde edebilirsiniz, çünkü PostgreSQL, birleştirme işleminin alt sorguda tanımlananla aynı sırada olacağına dair bir garanti veriyor gibi görünüyor. Bu oldukça havalı. Bu bölümlenmiş tablolar ve / veya FDW tablolar ve / veya paralel çalışanlar durumunda hala devam edip etmediğini ve PostgreSQL'in gelecek vaatlerde bu sözü tutabileceğini merak ediyorum.
pbillen

Kayıt için hiçbir şekilde kabul edilen cevaptan şüphe etme niyetim yoktu. Sadece indekslerin kümelenme ile birlikte varlığı ve kullanımı hakkındaki akla iyi bir katkı olduğunu düşündüm.
pbillen

1
Bu ise iyi bir ek.
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.