2017-06-01 3 views
1

Je crée une procédure pour analyser les données d'entrée json et les stocker dans des tables. La fonction ressemble:Postgresql Insert select avec plusieurs lignes

create or replace function test_func(d json) 
returns void as $$ 
    begin 
    with n as (
    insert into t1 (name) values (d::json -> 'name') returning id 
    ), c as (
    insert into t2 (cars) values json_array_elements_text(d::json -> 'cars') returning id 
    ) 
    insert into t3 (id, name_id, cars_id, brand) 
    select 1, n.id, c.id, json_array_elements_text(d::json -> 'brands') from n, c; 
end; 
$$ language plpgsql; 


CREATE TABLE t1 
(
    "id" SERIAL PRIMARY KEY, 
    "name" text NOT NULL 
) 

CREATE TABLE t2 
(
    "id" SERIAL PRIMARY KEY, 
    "cars" text NOT NULL, 
    "car_type" int 
) 

CREATE TABLE t3 
(
    "id" int, 
    "name_id" int REFERENCES t1(id), 
    "cars_id" int REFERENCES t2(id), 
    "brand" text 
) 

l'entrée de données est le nom que le texte, les voitures et les marques sont tableau, le tout enveloppé dans un JSON. Donc la dernière insertion a des types de valeurs mélangés, si la personne a deux voitures, j'ai 4 lignes insérées dans t3 parce que c.id et json_array_elements_text (d :: json -> 'brands') ont deux ensembles de données, 2x2 = 4 , comment puis-je mapper la valeur insérée à un sur un? donc 1er c.id devrait être mappé à la 1ère marque.

+0

Pouvez-vous ajouter un exemple de votre JSON, résultat réel et le résultat attendu? et éventuellement la description de vos tables. – cachique

+0

Bien sûr, t1 stocke id et nom: texte, t2 a id et voitures: texte (chaque voiture a sa propre ligne), t3 essentiellement lien t1 et t2 ajoutant des informations de marque, t3: id: int, name_id (fk à t1), cars_id (fk à t2) et marque: texte. Le json ressemble à {nom: john, voitures: ["bmw X5 xdrive", "volvo v90 rdesign"]}, marques: ["bmw", "volvo"]}. Attendez-vous à ce que t3 associe un car_id au même index de marque unique que dans le tableau json. cette fonction insère 4 rangées dans t3, chaque voiture apparaît deux fois avec des marques différentes – Wizer

Répondre

0

Pour les mapper, vous devez vous joindre non sur true, mais sur une ligne distincte.

Voici un exemple comment joindre les deux sur l'ID with ordinality - espérons que cela aidera. en fonction de votre échantillon JSON

t=# with j as (select '{"name":"john", "cars":["bmw X5 xdrive","volvo v90 rdesign"], "brands":["bmw","volvo"]}'::json d) 
select car,brand,t1.id from j 
join json_array_elements_text(j.d->'cars') with ordinality t1(car,id) on true 
join json_array_elements_text(j.d->'brands') with ordinality t2(brand,id) on t1.id = t2.id 
; 
     car  | brand | id 
-------------------+-------+---- 
bmw X5 xdrive  | bmw | 1 
volvo v90 rdesign | volvo | 2 
(2 rows) 

mise à jour élaboration pour OP:

vous pouvez éviter mappage de plusieurs lignes, en agrégeant em, puis en utilisant l'index:

votre fn:

create or replace function test_func(d json) 
returns void as $$ 
    begin 
with j as (select d) 
, a as (
    select car,brand,t1.id oid 
    from j 
    join json_array_elements_text(j.d->'cars') with ordinality t1(car,id) on true 
    join json_array_elements_text(j.d->'brands') with ordinality t2(brand,id) on t1.id = t2.id 
) 
, n as (
    insert into t1 (name) values (d::json -> 'name') returning id 
), c as (
    insert into t2 (cars) select car from a order by oid returning id 
) 
, ag as (
    select array_agg(c.id) cid from c 
) 
insert into t3 (id, name_id, cars_id, brand) 
    select 1, n.id,cid[oid], brand 
    from a 
    join n on true 
    join ag on true 
; 
end; 
$$ language plpgsql; 

vos tables:

CREATE TABLE t1 ("id" SERIAL PRIMARY KEY, "name" text NOT NULL); 
CREATE TABLE t2 ("id" SERIAL PRIMARY KEY, "cars" text NOT NULL); 
CREATE TABLE t3 ("id" int, "name_id" int REFERENCES t1(id), "cars_id" int REFERENCES t2(id), "brand" text); 

exécution:

t=# select test_func('{"name":"john", "cars":["bmw X5 xdrive","volvo v90 rdesign"], "brands":["bmw","volvo"]}'); 
test_func 
----------- 

(1 row) 

t=# select * from t1; 
id | name 
----+-------- 
14 | "john" 
(1 row) 

t=# select * from t2; 
id |  cars 
----+------------------- 
27 | bmw X5 xdrive 
28 | volvo v90 rdesign 
(2 rows) 

t=# select * from t3; 
id | name_id | cars_id | brand 
----+---------+---------+------- 
    1 |  14 |  27 | bmw 
    1 |  14 |  28 | volvo 
(2 rows) 
+0

Merci Vao, il fait briller de la lumière, mais j'ai essayé d'ajouter avec ordinality t3 insert, qui ne semble pas fonctionner. Pouvez-vous s'il vous plaît suivre mon code pour insérer dans t1 et t2 puis utiliser les identifiants de retour alignant avec les valeurs de tableau à insérer dans t3? – Wizer

+0

sûr, s'il vous plaît ajouter des définitions de tableaux à votre poste, afin que je puisse reproduire votre fonction –

+0

Merci beaucoup Vao! Cela fait tout mon sens – Wizer