Reputation: 4916
I have three tables, which I create with these queries:
create table parents(
rowid serial primary key,
display_name varchar,
unique(display_name)
);
create table clients(
rowid serial primary key,
parent int,
display_name varchar,
foreign key (parent) references parents(rowid),
unique(display_name, parent)
);
create table datapoints(
rowid serial primary key,
client int,
val float8,
foreign key (client) references clients(rowid)
);
I am getting data which contains parent name, client name and some value. Each time I get this data, I want to add rows to the datapoints table. I also want to add rows to the clients and parents table, but only if the data I received has unrecognized names.
For example, I might get this data:
"parent1-client1-123.0"
What I would like to achieve is to process this data (in a query), this way:
Insert a row to the "parents" table, if needed.
Insert a row to the "clients" table with the appropriate parent id (obtained in the previous step?), if needed.
Insert a row to the "datapoints" table with the appropriate client id (obtained in the previous step?).
How can I manage this with queries? The programmer in me wants to write:
But obviously this is not how sql works, right? And what if, instead of these three "nested" tables I have 5, or 10?(!)
Please help
Upvotes: 0
Views: 682
Reputation: 14886
Postgres can return the generated key on an insert. Unfortunately, when it encounters an existing unique key and an on constraint
clause it does not return the existing key. (A guess, but you have probably discovered this.) However the programmer in you is not far off. Instead of retrieve and save the id you retrieve retrieve and use directly via a select. If needed for subsequent operations you just 'repeat' the retrieval. So with 3 levels you retrieve the first level value (parent rowid) for the 2nd level (client) and 3rd level (datapoints). The best (imho) just create a procedure which handles the 3 levels. Further, break down the input into is components before calling the procedure. The resulting procedure becomes:
create or replace procedure establisth_parent_client_datapoint(
parent_name_in varchar
, client_name_in varchar
, val_in float8
)
language sql
as $$
insert into parents (display_name)
values (parent_name_in)
on conflict (display_name)
do nothing;
insert into clients(display_name, parent)
select client_name_in
, p.rowid
from parents p
where p.display_name = parent_name_in
on conflict (display_name, parent)
do nothing;
insert into datapoints(val, client)
select val_in, c.rowid
from parents p
join clients c
on ( c.parent = p.rowid)
where c.display_name = client_name_in
and p.display_name = parent_name_in;
$$;
since I do not know your development language I will supply examples in a Postgres DO block
. See demo here.
As far as having 5-10 nested tables, you just need to repeat the basic process for each. Of course with 10 nested tables, I would probably question the design.
Upvotes: 1