Table :
CREATE TABLE profit_center
(
id serial NOT NULL,
name character varying,
site_id integer,
CONSTRAINT profit_center_pkey PRIMARY KEY (id),
CONSTRAINT profit_center_name_site_id_key UNIQUE (name, site_id)
);
http://sqlfiddle.com/#!17/04630/4
Query which I have tried :
select site_id,json_build_object(name,id) as jsn from profit_center;
I want to group by all the jsn values by site_id. Expected result in single row :
{
"1": {
"Darshan": "1",
"ABC": 2
},
"2": {
"XYZ": 3
}
}
looks bad, but returns your expected result:
with r as (select concat('"',site_id,'": ',concat('{',string_agg(concat('"',name,'": ',id),','),'}')::json) from profit_center group by site_id)
select concat('{',string_agg(concat,','),'}')::json from r;
Related
I am using Postgres, In my code below I want to insert into the database only if the id doesn't already exist.
DROP TABLE json_table;
CREATE temp TABLE json_table (
id VARCHAR(50) PRIMARY KEY,
str_col VARCHAR(500),
int_col SMALLINT,
bool_col BOOLEAN,
json_col JSON,
float_col DECIMAL
);
INSERT INTO json_table
SELECT * FROM json_populate_recordset (NULL::json_table,
'{ "insert": [
{
"id": "1",
"str_col": "Postgres bar data",
"int_col": 3151,
"bool_col": false,
"json_col": {
"data": "tutorials"
},
"float_col": 11.51099159756918
},
{
"id": "2",
"str_col": "Postgres tutorials data data",
"int_col": 4237,
"bool_col": true,
"json_col": {
"type": "type"
},
"float_col": 48.94065780742467
}
]}'::json->'insert');
SELECT * FROM json_table;
You can use the on conflict do nothing clause. Your id column is the primary key of the table, so this would just work:
INSERT INTO json_table
SELECT *
FROM json_populate_recordset (NULL::json_table,
'{ "insert": [
{
"id": "1",
"str_col": "Postgres bar data",
"int_col": 3151,
"bool_col": false,
"json_col": {
"data": "tutorials"
},
"float_col": 11.51099159756918
},
{
"id": "2",
"str_col": "Postgres tutorials data data",
"int_col": 4237,
"bool_col": true,
"json_col": {
"type": "type"
},
"float_col": 48.94065780742467
}
]}'::json->'insert')
ON CONFLICT (id) DO NOTHING;
Demo on DB Fiddle
Say this record was inserted before:
insert into json_table(id) values(1)
The above query executes without error, and inserts the second row only.
Define a unique constraint on id:
alter table t add constraint unq_json_table_id unique(id);
You can then insert using on conflict:
insert into json_table . . .
. . .
on conflict (id) do nothing;
I use PostgreSQL 10.11 and would want to enter the following structure into a jsonb field:
{
lead: {
name: string,
prep: boolean
},
secondary: {
{
name: string,
prep: boolean
},
{
name: string,
prep: boolean
}
}
so lead is an object with name and prep and secondary is an array of name and preps.
How can I do that? The scripts below is to create a table with jsonb field:
CREATE TABLE public.test01 (
name JSONB DEFAULT '{}'::jsonb NOT NULL
)
WITH (oids = false);
ALTER TABLE public.test01
ALTER COLUMN id SET STATISTICS 0;
COMMENT ON COLUMN public.test01.name
IS '''[]''';
ALTER TABLE public.test01
OWNER TO postgres;
I'm trying this insert but get error:
INSERT INTO
public.test01
(
name
)
VALUES
('
{"lead":
"name": "Paint house",
"prep": "yes"}
,
"Secondary":
"name": "John",
"prep", "No"}
}
');
It's the first time I'm using jsonb so a select example would also be helpful to know hoe to read the data as well.
Your JSON is malformed. Presumably, you meant:
INSERT INTO public.test01 (name)
VALUES (
'{
"lead": {
"name": "Paint house",
"prep": "yes"
},
"Secondary": {
"name": "John",
"prep": "No"
}
}'::jsonb);
Demo on DB Fiddle
I have the following JSON which is stored in a jsonb field named "Data" in a PostgreSQL database:
{
"CompetitionData" :
{
"StartDate" : "12.06.2018",
"Name" : "TestCompetition",
"Competitors" :
[
{
"Id" : "100",
"Name" : "John",
"Age" : "24",
"Score" : "98",
"Shoes":
{
"Brand" : "Nike"
}
},
{
"Id" : "200",
"Name" : "Adam",
"Age" : "32",
"Score" : "78",
"Shoes":
{
"Brand" : "Adidas"
}
}
]
}
}
Im trying to get a specific entry in Competitors, like e.g.
SELECT * FROM Competitors WHERE Shoes = "Nike";
And the result must look like this:
{
"Id" : "100",
"Name" : "John",
"Age" : "24",
"Score" : "98",
"Shoes":
{
"Brand" : "Nike"
}
}
I tried the following query, but i keeps returning all competitors:
SELECT jsonb_array_elements(public."Competitions"."Data"->'CompetitionData'->'Competitors') as test
FROM public."Competitions" WHERE public."Competitions"."Data" #> '{"CompetitionData":{"Competitors":[{"Shoes":{"Brand":"Nike"}}]}}';
Is it possible to return just the competitor with Shoe Brand "Nike" ?
Use jsonb_array_elements in the from clause
SELECT j.* FROM
t cross join lateral
jsonb_array_elements(data->'CompetitionData'->'Competitors') as j(comp)
where j.comp->'Shoes'->>'Brand' = 'Nike'
Demo
Why do you want to save it in jsonb? Just normalize it into a database:
CREATE TABLE competitor_shoe (
name text PRIMARY KEY,
brand text NOT NULL
);
CREATE TABLE competitor (
id int PRIMARY KEY,
name text NOT NULL,
age int NOT NULL,
score int NOT NULL,
shoe text NOT NULL REFERENCES competitor_shoe(name)
);
CREATE TABLE competition (
name text PRIMARY KEY,
start_date date NOT NULL
);
CREATE TABLE competition_competitor (
competition text REFERENCES competition,
competitor int REFERENCES competitor,
PRIMARY KEY (competition,competitor)
);
INSERT INTO competitor_shoe
VALUES ('shoes1', 'Nike'),
('shoes2', 'Adidas');
INSERT INTO competitor
VALUES (100,'John',24,98,'shoes1'),
(200,'Adam',32,78,'shoes2');
INSERT INTO competition
VALUES (
'TestCompetition',
'12.06.2018'
);
INSERT INTO competition_competitor
VALUES ('TestCompetition', 100), ('TestCompetition', 200);
-- query the data
SELECT *
FROM competitor c
JOIN competitor_shoe cs
ON c.shoe = cs.name
WHERE brand = 'Nike';
-- query the data and return it as json object
SELECT to_jsonb(c) || jsonb_build_object('shoe', to_jsonb(cs)) as data
FROM competitor c
JOIN competitor_shoe cs
ON c.shoe = cs.name
WHERE brand = 'Nike';
This is a follow-up question of Extract all values from json in sql table
What if the json value has multiple levels?
For example,
{
"store-1": {
"Apple": {
"category": "fruit",
"price": 100
},
"Orange": {
"category": "fruit",
"price": 80
}
},
"store-2": {
"Orange": {
"category": "fruit",
"price": 90
},
"Potato": {
"category": "vegetable",
"price": 40
}
}
}
In this case, I want to extract the price for all the items. But I get error when I run the below query.
with my_table(items) as (
values (
'{"store-1":{"Apple":{"category":"fruit","price":100},"Orange":{"category":"fruit","price":80}},
"store-2":{"Orange":{"category":"fruit","price":90},"Potato":{"category":"vegetable","price":40}}}'::json
)
)
select key, (value->value->>'price')::numeric as price
from my_table,
json_each(json_each(items))
I get the below error.
ERROR: function json_each(record) does not exist
LINE 10: json_each(json_each(items))
If I remove one json_each(), it throws
ERROR: operator does not exist: json -> json
LINE 8: select key, (value->value->>'price')::numeric as price
You can use lateral join, something like:
with my_table(items) as (
values (
'{"store-1":{"Apple":{"category":"fruit","price":100},"Orange":{"category":"fruit","price":80}},
"store-2":{"Orange":{"category":"fruit","price":90},"Potato":{"category":"vegetable","price":40}}}'::json
)
)
select outer_key, key, value->>'price' from (
select key as outer_key, value as val from my_table
join lateral json_each(items)
on true
)t
join lateral json_each(val)
on true
I am using Postgres 9.5, and I have the following tables:
Users
id UUID
name TEXT
Images
id UUID
key TEXT
width INTEGER
height INTEGER
Posts
id UUID
title TEXT
author_id UUID
content JSONB
The posts' content is like:
[
{ "type": "text", "text": "learning pg" },
{ "type": "image", "image_id": "8f4422b4-3936-49f5-ab02-50aea5e6755f" },
{ "type": "image", "image_id": "57efc97c-b9b4-4cd5-b1e1-3539f5853835" },
{ "type": "text", "text": "pg is awesome" }
]
Now I want to join the image type of content, and populate them with image_id, like:
{
"id": "cb1267ca-b1ac-4daa-8c7e-72d4c000e9fa",
"title": "Learning join jsonb in Postgres",
"author_id": "deba01b7-ec58-4cc2-b3ae-7dc42e582767",
"content": [
{ "type": "text", "text": "learning pg" },
{
"type": "image",
"image": {
"id": "8f4422b4-3936-49f5-ab02-50aea5e6755f",
"key": "/upload/test1.jpg",
"width": 800,
"height": 600
}
},
{
"type": "image",
"image": {
"id": "57efc97c-b9b4-4cd5-b1e1-3539f5853835",
"key": "/upload/test2.jpg",
"width": 1280,
"height": 720
}
},
{ "type": "text", "text": "pg is awesome" }
]
}
Here is my test sql file:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
DROP TABLE IF EXISTS Users;
DROP TABLE IF EXISTS Images;
DROP TABLE IF EXISTS Posts;
CREATE TABLE Users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name text NOT NULL
);
CREATE TABLE Images (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
key TEXT,
width INTEGER,
height INTEGER,
creator_id UUID
);
CREATE TABLE Posts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title TEXT,
author_id UUID,
content JSONB
);
DO $$
DECLARE user_id UUID;
DECLARE image1_id UUID;
DECLARE image2_id UUID;
BEGIN
INSERT INTO Users (name) VALUES ('test user') RETURNING id INTO user_id;
INSERT INTO Images (key, width, height, creator_id) VALUES ('upload/test1.jpg', 800, 600, user_id) RETURNING id INTO image1_id;
INSERT INTO Images (key, width, height, creator_id) VALUES ('upload/test2.jpg', 600, 400, user_id) RETURNING id INTO image2_id;
INSERT INTO Posts (title, author_id, content) VALUES (
'test post',
user_id,
('[ { "type": "text", "text": "learning pg" }, { "type": "image", "image_id": "' || image1_id || '" }, { "type": "image", "image_id": "' || image2_id || '" }, { "type": "text", "text": "pg is awesome" } ]') :: JSONB
);
END $$;
Is there any way to implement this requirement?
SELECT jsonb_pretty(to_jsonb(p)) AS post_row_as_json
FROM (
SELECT id, title, author_id, c.content
FROM posts p
LEFT JOIN LATERAL (
SELECT jsonb_agg(
CASE WHEN c.elem->>'type' = 'image' AND i.id IS NOT NULL
THEN elem - 'image_id' || jsonb_build_object('image', i)
ELSE c.elem END) AS content
FROM jsonb_array_elements(p.content) AS c(elem)
LEFT JOIN images i ON c.elem->>'type' = 'image'
AND i.id = (elem->>'image_id')::uuid
) c ON true
) p;
How?
Unnest the jsonb array, producing 1 row per array element:
jsonb_array_elements(p.content) AS c(elem)
For each element LEFT JOIN to images on the conditions that
... the key 'type' has the value 'image': c.elem->>'type' = 'image'
... the UUID in image_id matches: i.id = (elem->>'image_id')::uuid
An invalid UUID in content would raise an exception.
For image types, where a matching image was found
c.elem->>'type' = 'image' AND i.id IS NOT NULL
remove the key 'image_id' and add the related image row as jsonb value:
elem - 'image_id' || jsonb_build_object('image', i)
Else keep the original element.
Re-aggregate the modified elements to a new content column with jsonb_agg().
Would work with a plain ARRAY constructor as well.
Unconditionally LEFT JOIN LATERAL the result to posts and select all columns, only replace p.content with the generated replacement c.content
In the outer SELECT, convert the whole row to jsonb with a simple to_jsonb().
jsonb_pretty() is only for human-readable representation and totally optional.
All jsonb functions are documented in the manual.