2015-06-29 12 views
20

dizi_agg yerine [null] yerine Postgres'te bazı nesneler ve etiketler seçiyorum.Postgres, birleştirme tablosu

nesnelerid

etiketlemeleriid | object_id | tag_id

etiketlerid | tag

Ben toplamak için array_agg kullanarak, böyle tablolar katılacağım: şema oldukça basit, üç tablo olduğunu etiketleri bir alana dönüştürün:

0 Nesne herhangi bir etiket olup olmadığını
SELECT objects.*, 
    array_agg(tags.tag) AS tags, 
FROM objects 
LEFT JOIN taggings ON objects.id = taggings.object_id 
LEFT JOIN tags ON tags.id = taggings.tag_id 

Ancak Postgrees bu döndürür: yerine boş bir dizinin

[ null ] 

. Etiket yokken boş bir diziyi nasıl döndürebilirim? İade edilen bir boş etiketin olmadığını kontrol ettim.

aggregate docs, "Birleştirme işlevi sıfır olduğunda veya boş bir diziyi gerektiğinde boş bırakmak için kullanılabilir". COALESCE(ARRAY_AGG(tags.tag)) as tags'u denedim, ancak yine de null olan bir dizi döndürüyor. Ikinci parametre çok sayıda şeyler (örneğin, COALESCE(ARRAY_AGG(tags.tag), ARRAY()) yapmayı denedim, ancak hepsi sözdizimi hatalarıyla sonuçlanır.

cevap

17

ile

array_agg(tags.tag) 

yerine array_remove(..., NULL)tags.tag olan (introduced in 9.3) eğer NOT NULL (aksi takdirde ancak içinde, dizideki NULL değerleri tutmak isteyebilirsiniz durumda, tek bir mevcut NULL etiketi ve LEFT JOIN nedeniyle bir NULL etiketi arasında ayrım yapamazsınız:

SELECT objects.*, 
    array_remove(array_agg(tags.tag), NULL) AS tags, 
FROM objects 
LEFT JOIN taggings ON objects.id = taggings.object_id 
LEFT JOIN tags ON tags.id = taggings.tag_id 

Etiket bulunamazsa, boş bir dizi döndürülür.

+0

Bu cevabı, belki de diğerleri için haksız bir şekilde seçtim çünkü çok daha az sorgu değişikliği içeriyor ve buradaki boş etiket durumu umurumda değil. –

+0

Bu benim de kullandığım cevaptır, ancak 'neden' hakkında daha fazla bilgi edinmek isteyenler için, aşağıdaki fonksiyonun yanı sıra Patrick'ın yanıtına bakınız: https://www.postgresql.org/docs/9.5/static/functions- aggregate.html –

1

Belgeler, NULL içeren bir dizinin döndüğünü bildirir. Bunu boş bir diziye dönüştürmek isterseniz, bazı küçük büyü yapmak gerekir.

Bu etiketler text türü (veya çeşitlerinin herhangi biri) olduğunu varsayar
SELECT objects.id, 
    CASE WHEN length((array_agg(tags.tag))[1]) > 0 
    THEN array_agg(tags.tag) 
    ELSE ARRAY[]::text[] END AS tags 
FROM objects 
LEFT JOIN taggings ON objects.id = taggings.object_id 
LEFT JOIN tags ON tags.id = taggings.tag_id 
GROUP BY 1; 

; gerektiği gibi döküm değiştirmek

burada hile olduğunu (ilk ve yalnızca) [NULL] dizisindeki öğe 0 uzunluğundadır, Herhangi bir veri tags'dan döndürülür, toplu olarak döndürürseniz, aksi halde doğru türde boş bir dizi oluşturun.

arada, coalesce() kullanmayla ilgili belgelerinde deyimi köhne biraz: kastedilmektedir ne sonuç olarak NULL istemiyorsanız, bir 0 veya diğer bazı çıkışına dönüfltürmeye coalesce() kullanabileceği aşağıdadır seçimi. Ancak bunu, sizin durumunuzda bir çözüm sağlayamayacak olan dizi öğeleri yerine dizisine uygulamanız gerekir.

+1

Yep soru gerçekten olarak ise, aslında coalesce', 'nullif',' tersini istiyorum belirir. –

+0

'length (NULL)' 'NULL',' 0' değil. length (NULL)> 0 'ayrıca' ELSE 'durumuna düşecek olan "NULL". Fakat uzunluk (''> 0 ') da öyle, ve bunun istenen davranış olduğunu düşünmüyorum. –

+0

https://www.postgresql.org/docs/9.5/static/functions-aggregate.html Belgelere gömülü ama ilgili metin "Sayım dışında, bu işlevlerin boş değer döndürdüğü unutulmamalıdır." Hiçbir satır seçildiğinde, özellikle, hiçbir satırın toplamı, beklendiği gibi sıfır değil, boş bir dizi yerine boş bir dizi yerine boş döndürür. –

4

Dokümanlar, sıfır satırı topladığınızda, boş bir değer aldığınızı ve COALESCE kullanımıyla ilgili notun bu özel durumu ele aldığını söylüyor.

Bunun nedeni şekilde bir LEFT JOIN davranacağını ait sorgunuza geçerli değildir - bu sıfır eşleşen satırları bulduğunda, bu boş değerlere dolu bir satır döndürür (ve bir boş satır agrega bir dizidir bir boş öğe ile).

Sen körlemesine çıktıda [] ile [NULL] yerine cazip, ancak o zaman ve tags.tag boş olan nesneleri etiketlenmiş hiçbir etiketleriyle nesneler arasında analitik yöntemlerle ayırt yeteneğini kaybedebilir. Uygulama mantığınız ve/veya bütünlük kısıtlamalarınız bu ikinci duruma izin vermeyebilir, ancak içeri sızmayı başarırsa boş etiketin bastırılmamasının daha fazla nedeni budur.

Etiketsiz bir nesneyi tanımlayabilirsiniz (veya genel olarak, birleştirme koşulunun diğer tarafındaki alanın boş olup olmadığını kontrol ederek bir LEFT JOIN eşleşme bulunmadığını söyleyin. senin durumunda Yani, sadece bir başka seçenek olabilir

CASE 
    WHEN taggings.object_id IS NULL 
    THEN ARRAY[]::text[] 
    ELSE array_agg(tags.tag) 
END 
+1

Bence bu daha iyi bir açıklama ve cevaptır, nottax hatalarından kaçınmak için 'GROUP BY' deyimine' taggings.object_id' eklenmesini gerektirdiğine dikkat etmeliyim 'ERROR: 42803: column "taggings.object_id" GROUP BY deyiminde görünür veya bir toplama işlevinde kullanılır - bu maddeyi eklemek sonuçların tümünü değiştirir mi? – user9645

+1

@ user9645: Özgün sorgu bir "GROUP BY objects.id" (bu aynı hatayı önlemek için gerekli olan) vardı, varsayarak "GROUP BY objects.id, taggings.object_id", gruplandırmayı etkilemez "JOIN" koşulu, belirli bir 'objects.id' değerinin hiçbir zaman birden fazla farklı "taggings.object_id" değeriyle ilişkilendirilememesini sağlar. –

+0

Nick - Teşekkürler diye düşündüm ama olumlu değildi. – user9645

1

Bunu yapacağım öğrendim: tags.tag bir metin türü olduğunu varsayarak

COALESCE(ARRAY_AGG(tags.tag), ARRAY[]::TEXT[]) 

....

Belki bu eski Postgres sürümlerinde çalışmayabilir, ancak ben bunu kullanıyorum emin değilim. 9.6 ve daha önce sağlanan CASE WHEN x IS NULL... GROUP BY... çözümden daha az hantal ve çalışıyor gibi görünüyor. 9.4 biri belli bir kritere uyum satırları Devam etmek için bir toplama işlevi çağrısı kısıtlayabilir yana

+0

Bu cevabı bulmaktan daha kolay oldu Dokümanlar – boatcoder

+0

'dan daha kolay. Bu benim için hiçbir şey yapmıyorsa 9.6, yine de (bu durumda etiketlerde) değer yoksa {NULL} 'döndürür. – Inkling

3

: array_agg(tags.tag) filter (where tags.tag is not null)