PostgreSQL dönüş sonucu JSON dizisi olarak ayarlandı mı?


135

PostgreSQL'in bir sorgunun sonucunu bir JSON dizisi olarak döndürmesini istiyorum. verilmiş

create table t (a int primary key, b text);

insert into t values (1, 'value1');
insert into t values (2, 'value2');
insert into t values (3, 'value3');

Benzer bir şey isterim

[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]

veya

{"a":[1,2,3], "b":["value1","value2","value3"]}

(aslında ikisini de bilmek daha faydalı olacaktır). Gibi bazı şeyler denedim

select row_to_json(row) from (select * from t) row;
select array_agg(row) from (select * from t) row;
select array_to_string(array_agg(row), '') from (select * from t) row;

Ve yakın olduğumu hissediyorum, ama gerçekten orada değil. 9.15 dışındaki diğer belgelere bakmalı mıyım ? JSON İşlevleri ve Operatörleri ?

Bu arada, fikrimden emin değilim. Bu olağan bir tasarım kararı mı? Benim düşünceme göre, tabii ki yukarıdaki 3 sorgunun ilkinin sonucunu (örneğin) alıp istemciye sunmadan önce uygulamada biraz değiştirebilirim, ancak PostgreSQL son JSON nesnesini doğrudan oluşturabilirse, daha basit olurdu, çünkü hala uygulamama herhangi bir JSON kitaplığına herhangi bir bağımlılık dahil etmedim.


1
Artık beta 1 sürümünde mevcut olan PG 9.4, ikili G / Ç dahil olmak üzere JSON için geliştirilmiş desteğe sahiptir. Bir geliştirme makinesindeyseniz, kontrol etmek isteyebilirsiniz.
Patrick

@Patrick: teşekkür ederim, json_object () 9.4'te yeni bir işlev gibi görünüyor ve SELECT json_object (array_agg (ta), array_agg (tb)) FROM t gibi bir şey denerdim, eğer sahip olsaydım
mühendisX

Yanıtlar:


266

TL; DR

SELECT json_agg(t) FROM t

JSON nesneler dizisi için ve

SELECT
    json_build_object(
        'a', json_agg(t.a),
        'b', json_agg(t.b)
    )
FROM t

dizilerin JSON nesnesi için.

Nesnelerin listesi

Bu bölümde, her satırın tek bir nesneye dönüştürüldüğü bir JSON nesne dizisinin nasıl oluşturulacağı açıklanmaktadır. Sonuç şuna benzer:

[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]

9.3 ve üstü

json_aggFonksiyon kutunun dışında bu sonucu üretir. Girdisini JSON'a nasıl dönüştüreceğini otomatik olarak anlar ve bir dizi halinde toplar.

SELECT json_agg(t) FROM t

jsonb(9.4'te tanıtılan) sürümü yoktur json_agg. Satırları bir dizi halinde toplayabilir ve sonra dönüştürebilirsiniz:

SELECT to_jsonb(array_agg(t)) FROM t

veya json_aggbir alçı ile birleştirin :

SELECT json_agg(t)::jsonb FROM t

Benim testlerim onları önce bir dizi halinde birleştirmenin biraz daha hızlı olduğunu gösteriyor. Bunun, dökümün tüm JSON sonucunu ayrıştırması gerektiğinden şüpheleniyorum.

9.2

9.2, json_aggveya to_jsonişlevlerine sahip değildir , bu nedenle eskisini kullanmanız gerekir array_to_json:

SELECT array_to_json(array_agg(t)) FROM t

İsteğe bağlı row_to_jsonolarak sorguya bir arama ekleyebilirsiniz :

SELECT array_to_json(array_agg(row_to_json(t))) FROM t

Bu, her satırı bir JSON nesnesine dönüştürür, JSON nesnelerini bir dizi olarak toplar ve ardından diziyi bir JSON dizisine dönüştürür.

İkisi arasında önemli bir performans farkı fark edemedim.

Listelerin amacı

Bu bölümde, her anahtarın tablodaki bir sütun olduğu ve her bir değerin sütunun değerlerinin bir dizisi olduğu bir JSON nesnesinin nasıl oluşturulacağı açıklanmaktadır. Şuna benzeyen sonuç:

{"a":[1,2,3], "b":["value1","value2","value3"]}

9.5 ve üstü

Şu json_build_objectişlevi kullanabiliriz:

SELECT
    json_build_object(
        'a', json_agg(t.a),
        'b', json_agg(t.b)
    )
FROM t

Ayrıca sütunları bir araya getirebilir, tek bir satır oluşturabilir ve ardından bunu bir nesneye dönüştürebilirsiniz:

SELECT to_json(r)
FROM (
    SELECT
        json_agg(t.a) AS a,
        json_agg(t.b) AS b
    FROM t
) r

Nesnenin istenen adlara sahip olduğundan emin olmak için dizilerin takma adının kesinlikle gerekli olduğunu unutmayın.

Hangisinin daha net olduğu bir fikir meselesidir. json_build_objectFonksiyonu kullanıyorsanız, okunabilirliği artırmak için bir satıra bir anahtar / değer çifti koymanızı şiddetle tavsiye ederim.

Ayrıca kullanabilirsiniz array_aggyerine json_agg, ancak test gösterir json_aggbiraz daha hızlıdır.

İşlevin bir jsonbsürümü yok json_build_object. Tek bir satırda toplayabilir ve şunları dönüştürebilirsiniz:

SELECT to_jsonb(r)
FROM (
    SELECT
        array_agg(t.a) AS a,
        array_agg(t.b) AS b
    FROM t
) r

Bu tür sonuçlar için diğer sorguların aksine, array_aggkullanırken biraz daha hızlı görünmektedir to_jsonb. Bunun JSON sonucunun ayrıştırılması ve doğrulanmasından kaynaklandığından şüpheleniyorum json_agg.

Veya açık bir atama kullanabilirsiniz:

SELECT
    json_build_object(
        'a', json_agg(t.a),
        'b', json_agg(t.b)
    )::jsonb
FROM t

to_jsonbVersiyon benim testine göre daha hızlı döküm önlemek için izin verir ve olduğu; Yine, bunun sonucu ayrıştırma ve doğrulama ek yükünden kaynaklandığından şüpheleniyorum.

9.4 ve 9.3

json_build_objectÖnceki sürümlerinde bir nesneye agrega ve dönüştürmek zorunda fonksiyonu, 9.5 yeniydi:

SELECT to_json(r)
FROM (
    SELECT
        json_agg(t.a) AS a,
        json_agg(t.b) AS b
    FROM t
) r

veya

SELECT to_jsonb(r)
FROM (
    SELECT
        array_agg(t.a) AS a,
        array_agg(t.b) AS b
    FROM t
) r

Eğer istemenize bağlı jsonveya jsonb.

(9.3'te yok jsonb.)

9.2

9.2'de bile to_jsonyok. Kullanmalısınız row_to_json:

SELECT row_to_json(r)
FROM (
    SELECT
        array_agg(t.a) AS a,
        array_agg(t.b) AS b
    FROM t
) r

belgeleme

JSON işlevleri için dokümanlara ulaşın JSON fonksiyonları .

json_aggüzerinde toplama işlevleri sayfa.

tasarlamak

Performans önemliyse, testlerime güvenmek yerine sorgularınızı kendi şemanız ve verilerinizle karşılaştırdığınızdan emin olun.

İyi bir tasarım olup olmadığı, gerçekten uygulamanıza bağlıdır. Sürdürülebilirlik açısından, belirli bir sorun görmüyorum. Uygulama kodunuzu basitleştirir ve uygulamanın bu bölümünde bakım yapılması gereken daha az şey olduğu anlamına gelir. Eğer PG size kutudan çıkardığınız gibi tam olarak ihtiyacınız olan sonucu verebilirse, kullanmamak için aklıma gelen tek sebep performans kaygısı olacaktır. Tekerleği ve hepsini yeniden icat etmeyin.

Boşlara

Toplama işlevleri genellikle NULLsıfır satırın üzerinde çalıştıklarında geri verir. Bu bir olasılıksa, COALESCEonlardan kaçınmak için kullanmak isteyebilirsiniz . Birkaç örnek:

SELECT COALESCE(json_agg(t), '[]'::json) FROM t

Veya

SELECT to_jsonb(COALESCE(array_agg(t), ARRAY[]::t[])) FROM t

Bunu işaret ettiği için Hannes Landeholm'a kredi


3
Cevabınız için teşekkür ederim. İkinci sorum olan SELECT row_to_json (row (array_agg (ta), array_agg (tb))) FROM t'nin cevabını bulmam için bana ilham verdin, ancak sonuçta etiket olarak a ve b yerine "f1" ve "f2" var.
mühendisX

@engineerX Cevabımı genişlettim.
jpmc26

3
Bazı durumlarda, iç seçim (t'den) sıfır satır döndürdüğünde boş bir JSON dizisi yerine NULL değerini geri almak istenmeyebilir. Bu, toplama işlevlerinin her zaman NULL döndürmesinden kaynaklanır ve birleştirme ile çözülebilir: array_to_json (coalesce (array_agg (t), array [] :: kayıt [])).
Hannes Landeholm

3
ve to_jsonyerine kullanabilirsiniz row_to_jsonarray_to_json
itsnikolay

(Birden çok) belirli sütunu seçmek için, bunları tek bir bağımsız değişken olarak iletmelisiniz - yuvarlak parantez listesi gibi SELECT json_agg((column1, column2, ...)) FROM t - fazladan parantezlere dikkat edin. Bu, "kullanıma hazır" açık olmayabilir.
jave.web

19

Ayrıca tablodan seçilen alanı ve bir araya toplanmasını istiyorsanız, o zaman dizi olarak.

SELECT json_agg(json_build_object('data_a',a,
                                  'data_b',b,
))  from t;

Sonuç gelecek.

 [{'data_a':1,'data_b':'value1'}
  {'data_a':2,'data_b':'value2'}]
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.