How to create new table with multiple columns from json table - postgresql

I have a table filled with json objects. There is only one column 'data'.
I would like to convert this table into one with multiple columns for all the keys in the json objects.
For example,
Right now my table is like this:
data
{'key1': 'value1', 'key2': 'value2'}
{'key1': 'value3', 'key2': 'value4'}
But I would like it like this:
key1
key2
value1
value2
value3
value4
I'm not trying to query it in this way, I would like to completely create a new table in the format that I've shown above.
I can try running:
INSERT INTO new_table
SELECT data -> 'key1', data -> 'key2'
FROM old_table
But since my json objects have hundreds of columns this may be inefficient. Is there a more efficient way to do so? Any help or suggestions is appreciated.

maybe this way is more simple?
select t.*
from yourtable
cross join json_to_record(jsoncol) t(key1 text,key2 text,key3 text,...);
on the second thought , if you already have a table that matches the columns from your json , you can do this:
create table newTable (key1 text, key2 text , ...);
select t.*
from yourtable
cross join json_populate_record(null::newTable,jsoncol) t;

Related

Abap: Select the same field from two db tables into one column of the internal table with one select

I'm new in using SQL in ABAP, so it must be a silly question, but is this somehow possible?:
as I'm new, I cannot add images directly.
Thanks a lot in advance, kind regards
It appears that what you want to do is create an SQL "Union": Select rows from two different database tables and put the results into one combined table. This is only possible if both SELECTs have the same fields. But you can usually accomplish that pretty easily by substituting the missing rows in each table by a constant value ('' in this example):
SELECT
key1
'' as key2
value1
value2
value3
'' as value4
FROM db1
UNION SELECT
key1
key2
value1
'' as value2
'' as value3
value4
FROM db2
INTO CORRESPONDING FIELDS OF TABLE #it.
When you are using an older release (<7.50), then you can combine the data of multiple SELECTs by using APPENDING instead of INTO:
SELECT
key1
value1
value2
value3
FROM db1
INTO CORRESPONDING FIELDS OF TABLE #it.
SELECT
key1
key2
value1
value4
FROM db2
APPENDING CORRESPONDING FIELDS OF TABLE #it.

PostgreSQL - JSON function

There's a different format for row_to_json and json_build_object.
For row_to_json the input is:
select ... <table.field> "<json_key"
And for the json_build_object:
"<json_key" , <table.field>
That's surprising to me. Oracle uses similar JSON functions with similar input. This may look like a minor thing, but it makes it harder for me to automate the generation of SELECT statements for complex/nested structures.
Consider this simple example:
CREATE TABLE "users" (
id SERIAL NOT NULL,
name VARCHAR(100),
email_address VARCHAR(150),
PRIMARY KEY(id)
);
INSERT INTO "users" ("id", "name", "email_address")
VALUES (1, 'user1', 'user1#mail.com'), (2, 'user2', 'user2#mail.com');
To fetch the email and the name where email is a dictionary object:
"{""user_name"":""user1"",""email"":{""email_address"" : ""user1#mail.com""}}"
The query would be:
select row_to_json(users) from
(select users.name "user_name" ,
(json_build_object( 'email_address' , users.email_address )) email
from users ) users;
I would like to use the same order of fields in both cases.
The input would be:
<table.field> "<json_key" OR "<json_key" <table.field>
I tried to flip the order using "AS" but it doesn't work:
select row_to_json(users) from
(select users.name "user_name" ,
(json_build_object( users.email_address as 'email_address' )) email
from users ) users;
Maybe with using only row_to_json() or json_build_object()?
Or anything else? (But no left join.)
row_to_json takes an entire row tuple (a record) as input, such as you get it from a subquery. If you don't use those, or don't like row_to_json, don't use it. It's a convenience mainly if you use SELECT * and not want to specify column/field names.
I recommend to use json_build_object with explicit field names, especially if you want to rename them anyway. Your query can and should be written as
SELECT json_build_object(
'user_name', users.name,
'email', json_build_object(
'email_address', users.email_address
)
) AS json_result
FROM users;

Can the categories in the postgres tablefunc crosstab() function be integers?

It's all in the title. Documentation has something like this:
SELECT *
FROM crosstab('...') AS ct(row_name text, category_1 text, category_2 text);
I have two tables, lab_tests and lab_tests_results. All of the lab_tests_results rows are tied to the primary key id integer in the lab_tests table. I'm trying to make a pivot table where the lab tests (identified by an integer) are row headers and the respective results are in the table. I can't get around a syntax error at or around the integer.
Is this possible with the current set up? Am I missing something in the documentation? Or do I need to perform an inner join of sorts to make the categories strings? Or modify the lab_tests_results table to use a text identifier for the lab tests?
Thanks for the help, all. Much appreciated.
Edit: Got it figured out with the help of Dmitry. He had the data layout figured out, but I was unclear on what kind of output I needed. I was trying to get the pivot table to be based on batch_id numbers in the lab_tests_results table. Had to hammer out the base query and casting data types.
SELECT *
FROM crosstab('SELECT lab_tests_results.batch_id, lab_tests.test_name, lab_tests_results.test_result::FLOAT
FROM lab_tests_results, lab_tests
WHERE lab_tests.id=lab_tests_results.lab_test AND (lab_tests.test_name LIKE ''Test Name 1'' OR lab_tests.test_name LIKE ''Test Name 2'')
ORDER BY 1,2'
) AS final_result(batch_id VARCHAR, test_name_1 FLOAT, test_name_2 FLOAT);
This provides a pivot table from the lab_tests_results table like below:
batch_id |test_name_1 |test_name_2
---------------------------------------
batch1 | result1 | <null>
batch2 | result2 | result3
If I understand correctly your tables look something like this:
CREATE TABLE lab_tests (
id INTEGER PRIMARY KEY,
name VARCHAR(500)
);
CREATE TABLE lab_tests_results (
id INTEGER PRIMARY KEY,
lab_tests_id INTEGER REFERENCES lab_tests (id),
result TEXT
);
And your data looks something like this:
INSERT INTO lab_tests (id, name)
VALUES (1, 'test1'),
(2, 'test2');
INSERT INTO lab_tests_results (id, lab_tests_id, result)
VALUES (1,1,'result1'),
(2,1,'result2'),
(3,2,'result3'),
(4,2,'result4'),
(5,2,'result5');
First of all crosstab is part of tablefunc, you need to enable it:
CREATE EXTENSION tablefunc;
You need to run it one per database as per this answer.
The final query will look like this:
SELECT *
FROM crosstab(
'SELECT lt.name::TEXT, lt.id, ltr.result
FROM lab_tests AS lt
JOIN lab_tests_results ltr ON ltr.lab_tests_id = lt.id'
) AS ct(test_name text, result_1 text, result_2 text, result_3 text);
Explanation:
The crosstab() function takes a text of a query which should return 3 columns; (1) a column for name of a group, (2) a column for grouping, (3) the value. The wrapping query just selects all the values those crosstab() returns and defines the list of columns after (the part after AS). First is the category name (test_name) and then the values (result_1, result_2). In my query I'll get up to 3 results. If I have more then 3 results then I won't see them, If I have less then 3 results I'll get nulls.
The result for this query is:
test_name |result_1 |result_2 |result_3
---------------------------------------
test1 |result1 |result2 |<null>
test2 |result3 |result4 |result5

Postgres: concatenate JSONB values across rows?

I'm getting grips with the JSONB functionality in Postgres >= 9.5 (and loving it) but have hit a stumbling block. I've read about the ability to concatenate JSON fields, so '{"a":1}' || '{"b":2}' creates {"a":1,"b":2}, but I'd like to do this across the same field in multiple rows. e.g.:
select row_concat_??(data) from table where field = 'value'
I've discovered the jsonb_object_agg function which sounds like it would be what I want, except that the docs show it taking multiple arguments, and I only have one.
Any ideas how I would do this? jsonb_agg creates an array successfully so it feels like I'm really close.
After some digging around with custom aggregates in Postgres, I have the following:
DROP AGGREGATE IF EXISTS jsonb_merge(jsonb);
CREATE AGGREGATE jsonb_merge(jsonb) (
SFUNC = jsonb_concat(jsonb, jsonb),
STYPE = jsonb,
INITCOND = '{}'
)
Which is then usable as:
SELECT group_id, jsonb_merge(data) FROM table GROUP BY group_id
Use jsonb_each():
with data(js) as (
values
('{"a": 1}'::jsonb),
('{"b": 2}')
)
select jsonb_object_agg(key, value)
from data
cross join lateral jsonb_each(js);
jsonb_object_agg
------------------
{"a": 1, "b": 2}
(1 row)

How to include the values of a select statement in an insert? (PostgreSQL)

I have this select statement:
select it.item_id
from item as it
where it.owning_collection in (select col.collection_id
from collection as col
where col.name like '%Restricted%'
Which returns around 3k results. Now I would like to make an insert in another table for each one of those results with that item_id as one of the parameters like this:
insert into metadatavalue (metadata_value_id, **item_id**, metadata_field_id, text_value, text_lang, place, confidence)
But since I'm not very experienced with databases, I'm not sure how to make these multiple inserts
All the other information needed in the insert statement are fixed values.
Table structures:
Table Item
*item_id
*submitter_id
*in_archive
*withdrawn
*last_modified
*owning_collection
*dicoverable
Table metadata
*metadata_value_id
*item_id
*metadata_field_id
*text_value
*text_lang
*place
*authority
*confidence
insert into metadatavalue (metadata_value_id, item_id, metadata_field_id, text_value, text_lang, place, confidence)
select 'metadata_value_id',it.item_id,'metadata_field_id','text_value', 'text_lang', 'place', 'confidence'
from item it
where it.owning_collection in (select col.collection_id
from collection as col
where col.name like '%Restricted%')
Replace 'apostrophed' columns with its default values.
Further reading.
INSERT INTO metadatavalue (item_id, metadata_field_id, text_value, text_lang, place, confidence)
SELECT it.item_id, <c1>, <c2>, <c3>, <c4>, <c5>
FROM item AS it
JOIN collection AS col ON col.collection_id = it.owning_collection
WHERE col.name LIKE '%Restricted%'
Where you replace <c1> etc with your constant values. Note also that I have rewritten your SELECT query to a more efficient JOIN.