insert into database only when id does not exist - sql

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;

Related

How to retrieve specific JSON value from a column containing a JSON array

I have this JSON array in a SQL Server table:
[
{
"FieldName": "DateCreated",
"FieldValue": "10/22/2020"
},
{
"FieldName": "IsMember",
"FieldValue": "false"
},
{
"FieldName": "EntityId",
"FieldValue": "ABC123"
}
]
I want to fetch only the FieldValue of the EntityId object, so the output should be only ABC123.
I have this query
SELECT JSON_VALUE(JsonColumnData, '$[2].FieldValue') AS EntityId
FROM MyTable
This returns the EntityId value, but the thing is that I have no guarantee that the EntityId will always be in the same position of the JSON array.
Is it possible to have the select return the EntityId regardless of its position in the JSON array?
You can use OPENJSON() to query the data and just filter the row(s) that contain EntityId...
create table dbo.MyTable (
JsonColumnData nvarchar(max)
);
insert dbo.MyTable (JsonColumnData) values (N'[
{
"FieldName": "DateCreated",
"FieldValue": "10/22/2020"
},
{
"FieldName": "IsMember",
"FieldValue": "false"
},
{
"FieldName": "EntityId",
"FieldValue": "ABC123"
}
]');
select FieldValue as EntityId
from dbo.MyTable
cross apply openjson(JsonColumnData) with (
FieldName nvarchar(11),
FieldValue nvarchar(11)
)
where FieldName = N'EntityId';
Which yields...
EntityId
---------
ABC123

How to verify primary key data in target table using Json source data and update with New values

Json data
{ "Root":
[
{ "Column": "Primary_key", "CurrentValue": "3456", "NewValue": null },
{ "Column": "FirstName", "CurrentValue": "Jon", "NewValue": null },
{ "Column": "Phone", "CurrentValue": "null", "NewValue": "6789" }
]
}
My Json data is from a table as shown above. I need to check if the primary key and FirstName matches a row, then update the value of column Phone in target row.
How to achieve this in SQL?
Create a table with specifications:
CREATE TABLE json_table (
<your_field_unique>,
CONSTRAINT <unique_cond> CHECK (<your_field_unique> IS JSON WITH UNIQUE KEYS)
);
Into <> you can put your variables.
Regards.

postgresql insert a structured data into jsonb

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

How to update JSONB array in postgres

I have a table with following definition:
CREATE TABLE USER_CONFIGURATIONS (
ID BIGSERIAL PRIMARY KEY,
DATA JSONB
);
I have a data field that looks like this :
[
{
"user_id": 1,
"user_name": "demo_user",
"is_manager": 1,
"options": [
{
"phone":{
"home":"XXXXXXX",
"work":"XXXXXXX"
},
"address":{
"home":"XXXXXXX",
"work":"XXXXXXX"
}
}
]
},
...
]
questions:
how to update "user_name" ?
how to update "options->phone->home" ?
UPDATE USER_CONFIGURATIONS SET DATA = jsonb_set(...) WHERE ...user_id=1;
postgres 9.6 version.
i tried with jsonb_set() but not wokring
https://www.db-fiddle.com/f/4ZYZiuJr4QgfNkzyTCeT1X/1
just run it twice:
update USER_CONFIGURATIONS
set data =
jsonb_set(
jsonb_set(
data,'{0,"user_name"}','"blah"'
), '{0,"options",0,"phone","home"}','999999'
)
where id =1
;

How to join jsonb array elements in Postgres?

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.