Json dizisi postgres dizisine nasıl çevrilir?


69

Ben bir sütun var databir tutan jsonböyle kabaca belgeyi:

{
    "name": "foo",
    "tags": ["foo", "bar"]
}

İç içe tagsdiziyi bitiştirilmiş bir dizeye ( foo, bar) dönüştürmek istiyorum. Bu array_to_string()teoride fonksiyon ile kolayca mümkün olurdu . Ancak, bu işlev jsondiziler üzerinde etkili olmaz . Yani bu nasıl açacağınızı acaba jsonbir Postgres içine dizi array?


Aradığın json_extract_path_text(your_column, 'tags') şey mi?
a_horse_with_no_name

1
@ a_horse_with_no_name: Kalan problem: dizi öğeleri JSON formatı için hala alıntı. Metin düzgün bir şekilde çıkarılmadı ...
Erwin Brandstetter

Yanıtlar:


94

Postgres 9.4 veya daha yeni

Açıkçası bu yazıdan ilham alan Postgres 9.4, eksik işlevleri ekledi:
Yama için Laurence Rowe ve kararlılık için Andrew Dunstan'a teşekkürler!

JSON dizisini yanlışlamak için. Sonra ondan bir Postgres dizisi oluşturmak için array_agg()veya bir ARRAY yapıcısı kullanın . Veya bir dize oluşturmak için .string_agg()text

LATERALVeya bir ilişkili alt sorguda satır başına toplanamayan öğeleri . Ardından orijinal sipariş korunur ve ihtiyacımız yoktur ORDER BY, GROUP BYhatta dış sorguda benzersiz bir anahtara da. Görmek:

'Json' ifadesini jsonbaşağıdaki tüm SQL kodlarında 'jsonb' ile değiştirin .

SELECT t.tbl_id, d.list
FROM   tbl t
CROSS  JOIN LATERAL (
   SELECT string_agg(d.elem::text, ', ') AS list
   FROM   json_array_elements_text(t.data->'tags') AS d(elem)
   ) d;

Kısa sözdizimi:

SELECT t.tbl_id, d.list
FROM   tbl t, LATERAL (
   SELECT string_agg(value::text, ', ') AS list
   FROM   json_array_elements_text(t.data->'tags')  -- col name default: "value"
   ) d;

İlgili:

İlişkili alt sorgudaki ARRAY yapıcısı:

SELECT tbl_id, ARRAY(SELECT json_array_elements_text(t.data->'tags')) AS txt_arr
FROM   tbl t;

İlgili:

İnce fark : nullElementler gerçek dizilerde korunur . Bu, değerleri textiçermeyen bir dize üreten yukarıdaki sorgularda mümkün değildir null. Gerçek gösterimi bir dizidir.

İşlev sargısı

Tekrarlanan kullanımda, bunu daha da kolaylaştırmak için, mantığı bir fonksiyonla kaplayın:

CREATE OR REPLACE FUNCTION json_arr2text_arr(_js json)
  RETURNS text[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY(SELECT json_array_elements_text(_js))';

Bunu bir SQL işlevi yapın , böylece daha büyük sorgularda sıraya alınabilir .
Daha IMMUTABLEbüyük sorgularda tekrarlanan değerlendirmeyi önlemek ve dizin ifadelerinde izin vermek için (çünkü) yapın.

Aramak:

SELECT tbl_id, json_arr2text_arr(data->'tags')
FROM   tbl;

db <> burada keman


Postgres 9.3 veya daha büyük

İşlevi kullanın json_array_elements(). Ama ondan çift ​​alıntılı dizge alıyoruz .

Dış sorguda toplama ile alternatif sorgu. CROSS JOINeksik veya boş dizileri olan satırları kaldırır. Öğeleri işlemek için de yararlı olabilir. Toplanacak benzersiz bir anahtara ihtiyacımız var:

SELECT t.tbl_id, string_agg(d.elem::text, ', ') AS list
FROM   tbl t
CROSS  JOIN LATERAL json_array_elements(t.data->'tags') AS d(elem)
GROUP  BY t.tbl_id;

ARRAY yapıcısı, hala alıntılanmış dizelerle:

SELECT tbl_id, ARRAY(SELECT json_array_elements(t.data->'tags')) AS quoted_txt_arr
FROM   tbl t;

nullYukarıdakilerin aksine, "null" metin değerine dönüştürüldüğünü not edin . Yanlış, kesinlikle konuşma ve belirsiz.

Zavallı adam şununla alıntı yapmıyor trim():

SELECT t.tbl_id, string_agg(trim(d.elem::text, '"'), ', ') AS list
FROM   tbl t, json_array_elements(t.data->'tags') d(elem)
GROUP  BY 1;

Tek bir satırı tbl'den al:

SELECT string_agg(trim(d.elem::text, '"'), ', ') AS list
FROM   tbl t, json_array_elements(t.data->'tags') d(elem)
WHERE  t.tbl_id = 1;

Dizeler ilişkili alt sorgu oluşturur:

SELECT tbl_id, (SELECT string_agg(trim(value::text, '"'), ', ')
                FROM   json_array_elements(t.data->'tags')) AS list
FROM   tbl t;

ARRAY yapıcı:

SELECT tbl_id, ARRAY(SELECT trim(value::text, '"')
                     FROM   json_array_elements(t.data->'tags')) AS txt_arr
FROM   tbl t;

Orijinal (eski) SQL Fiddle .
db <> burada keman .

İlgili:

Notlar (pg 9.4'ten beri eski)

Bir JSON dizisinden uygun değerleri döndürmek için bir json_array_elements_text(json), ikizi gerekir . Ancak bu JSON işlevlerinin sağladığı cephanelikten eksik gibi görünüyor . Veya skaler bir değerden bir değer çıkarmak için başka bir fonksiyon . Ben de onu özlüyorum. Bu yüzden doğaçlama yaptım , ama bu önemsiz olmayan durumlar için başarısız olacak ...json_array_elements(json)texttextJSON
trim()


Her zamanki gibi iyi bir yazı, ama içsellerin bildiği gibi neden array-> jsonb'dan gelen oyuncular değil. Sql-array daha kuvvetli yazıldığından diğer oyuncu kadrosunun uygulanmamasını anlayabiliyorum. Sırf PostgreSQL'in otomatik olarak kod üretmesine ters davranması mı (int [], bigint [], metin []) vb.
Evan Carroll

3
@Evan: array- to_jsonb()> jsonb dönüşümü için kullanırdın .
Erwin Brandstetter

Does SELECT ARRAY(SELECT json_array_elements_text(_js))gerçekten dizinin sipariş korunduğunu garanti? Optimizer’in, json_array_elements_text dosyasından çıkan satırların sırasını teorik olarak değiştirmesine izin verilmiyor mu?
Felix Geisendörfer

@Felix: SQL standardında resmi bir garanti yoktur. (daha sonra tekrar set işlevlerinin standart SQL'deki SELECT listesinde bile kullanılmasına izin verilmez.) ancak Postgres kılavuzunda gayri resmi bir iddia vardır. bkz: dba.stackexchange.com/a/185862/3684 açık olmak gerekirse - küçük Perfomance cezası pahasına - bkz: dba.stackexchange.com/a/27287/3684 . Şahsen, bu özel ifadenin Postgres’in 9.4’ten bu yana her gün ve gelecekteki sürümlerinde beklendiği gibi çalıştığından% 100 eminim.
Erwin Brandstetter

@ErwinBrandstetter bunu onayladığınız için çok teşekkür ederiz! Şu anda PostgreSQL tarafından sağlanan resmi ve gayri resmi garantileri sipariş eden garantileri özetleyen bir makale için araştırma yapıyorum ve cevaplarınız inanılmaz derecede yardımcı oldu! Makaleyi incelemek isterseniz bana bildirin, ancak endişelenmenize gerek yok. StackOverflow katkılarınız için minnettarım ve yıllar içinde sizden çok şey öğrendim!
Felix Geisendörfer

16

PG 9.4+

Kabul edilen cevap kesinlikle ihtiyacınız olan şey, ancak basitlik uğruna, buradaki için kullandığım bir yardımcı:

CREATE OR REPLACE FUNCTION jsonb_array_to_text_array(
  p_input jsonb
) RETURNS TEXT[] AS $BODY$

DECLARE v_output text[];

BEGIN

  SELECT array_agg(ary)::text[]
  INTO v_output
  FROM jsonb_array_elements_text(p_input) AS ary;

  RETURN v_output;

END;

$BODY$
LANGUAGE plpgsql VOLATILE;

O zaman sadece yapın:

SELECT jsonb_array_to_text_array('["a", "b", "c"]'::jsonb);

Cevabımı daha hızlı ifadeler ve daha basit bir fonksiyona ekledim. Bu büyük ölçüde daha ucuz olabilir.
Erwin Brandstetter

4
Bu işlev, saf SQL olmalıdır, böylece en iyi duruma getiricinin içine bakabilmesi gerekir. Burada pgplsql kullanmanıza gerek yoktur.
Böl

8

Bu soru PostgreSQL posta listelerinde sorulmuştu ve JSON alanını çıkarma operatörü aracılığıyla JSON metnini PostgreSQL metin türüne dönüştürmenin bu haksız yolu ile geldim:

CREATE FUNCTION json_text(json) RETURNS text IMMUTABLE LANGUAGE sql
AS $$ SELECT ('['||$1||']')::json->>0 $$;

db=# select json_text(json_array_elements('["hello",1.3,"\u2603"]'));
 json_text 
-----------
 hello
 1.3
 

Temel olarak, değeri tek elemanlı bir diziye dönüştürür ve sonra ilk eleman için ister.

Diğer bir yaklaşım da, bu alanı tüm alanları birer birer çıkarmak için kullanmak olacaktır. Ancak, büyük diziler için bu, her bir dizi elemanı için tüm JSON dizesini ayrıştırması gerektiği için O'nun daha yavaş olmasına neden olduğundan, daha yavaş olacaktır.

CREATE FUNCTION json_array_elements_text(json) RETURNS SETOF text IMMUTABLE LANGUAGE sql
AS $$ SELECT $1->>i FROM generate_series(0, json_array_length($1)-1) AS i $$;

db=# select json_array_elements_text('["hello",1.3,"\u2603"]');
 json_array_elements_text 
--------------------------
 hello
 1.3
 

1

Birkaç seçeneği test ettim. İşte en sevdiğim sorgu. Diyelim ki id ve json alanını içeren bir tablomuz var. Json alanı, pg dizisine dönüştürmek istediğimiz dizi içeriyor.

SELECT * 
FROM   test 
WHERE  TRANSLATE(jsonb::jsonb::text, '[]','{}')::INT[] 
       && ARRAY[1,2,3];

Her yerde çalışıyor ve diğerlerinden daha hızlı, ancak crutchy görünüyor)

Öncelikle json dizisi metin olarak yazılır ve sonra köşeli parantezleri parantez olarak değiştiririz. Sonunda, metin istenen tür dizisi olarak yayınlanıyor.

SELECT TRANSLATE('[1]'::jsonb::text, '[]','{}')::INT[];

ve eğer metni tercih ederseniz [] diziler

SELECT TRANSLATE('[1]'::jsonb::text, '[]','{}')::TEXT[];

2
SELECT TRANSLATE('{"name": "foo", "tags": ["foo", "bar"]}'::jsonb::text, '[]','{}')::INT[]; ERROR: malformed array literal: "{"name": "foo", "tags": {"foo", "bar"}}"Bunun nasıl çalışması gerektiği ile ilgili bazı açıklamalar eklemeniz gerektiğini düşünüyorum.
dezso

Soru JSON dizisinin (!) Pg dizisine nasıl çevrileceğidir. Farz edelim ki id ve jsonb sütunlarını içeren bir tablo var. JSONb sütunu, json dizisini içerir. Ardından
FiscalCliff 19

TRANSLATE (jsonb :: jsonb :: text, '[]', '{}') :: INT [], json dizisini pg dizisine dönüştürür.
FiscalCliff

SELECT translate('["foo", "bar"]'::jsonb::text, '[]','{}')::INT[]; ERROR: invalid input syntax for integer: "foo"
Bombaya

Bu diziler için [] metnini kullanmayı düşünün
FiscalCliff

0

Bu sorunun yanıtlarından alınan bu birkaç işlev kullanıyorum ve harika çalışıyorlar.

CREATE OR REPLACE FUNCTION json_array_casttext(json) RETURNS text[] AS $f$
    SELECT array_agg(x) || ARRAY[]::text[] FROM json_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION jsonb_array_casttext(jsonb) RETURNS text[] AS $f$
    SELECT array_agg(x) || ARRAY[]::text[] FROM jsonb_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION json_array_castint(json) RETURNS int[] AS $f$
    SELECT array_agg(x)::int[] || ARRAY[]::int[] FROM json_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION jsonb_array_castint(jsonb) RETURNS int[] AS $f$
    SELECT array_agg(x)::int[] || ARRAY[]::int[] FROM jsonb_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

Her birinde, boş bir dizi ile birleştirerek, beynimi bir süreliğine kırmamı sağlayan bir durumla ilgileniyorlar; bu durumda, boş bir diziyi ondan json/ jsonbondan çıkarmaya çalışırsanız , yerine hiçbir şey döndürmezsiniz. boş dizi ( {}) beklediğiniz gibi. Eminim onlar için bir miktar optimizasyon vardır, ancak kavramı açıklamada basitlik için olduğu gibi bırakılmıştır.

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.