How I can update one to many relation in Postgres? - sql

Here is my first table question.
CREATE TABLE "question" (
"question_id" serial NOT NULL,
"question" TEXT NOT NULL UNIQUE,
"added_at" TIMESTAMP NOT NULL,
"question_marks" integer NOT NULL,
CONSTRAINT "question_pk" PRIMARY KEY ("question_id"))
Questions have many options, so I referring every option row with question_id
CREATE TABLE "option" (
"option_id" serial NOT NULL,
"option" TEXT NOT NULL,
"option_question_id" integer NOT NULL,
"option_correct" BOOLEAN NOT NULL,
CONSTRAINT "option_pk" PRIMARY KEY ("option_id"))
ALTER TABLE "option" ADD CONSTRAINT "option_fk1" FOREIGN KEY ("option_question_id") REFERENCES "question"("question_id") ON DELETE CASCADE;
Now, How can I update both tables in one query?
I building an API. The below-given output is for request. The request will response with question details and options for the question.
I am able to update question but questions have many options, How can I update options?
"questionDetails": [
{
"question_id": 30,
"question": "What is gravity of Saturn?",
"added_at": "2020-02-20T18:30:00.000Z",
"question_marks": 1
}
],
"options": [
{
"option_id": 19,
"option": "20",
"option_question_id": 30,
"option_correct": true
},
{
"option_id": 20,
"option": "30",
"option_question_id": 30,
"option_correct": false
},
{
"option_id": 21,
"option": "40",
"option_question_id": 30,
"option_correct": false
},
{
"option_id": 18,
"option": "400000000",
"option_question_id": 30,
"option_correct": false
}
]
}
Now Can I update this relation?

You can chain multiple operations together in a single query by using CTEs that have returning clauses.
with
__parent as(
update
my_schema.parent_table
set
col_1 = 'a',
col_2 = 'b'
where
id_col = 3
returning
id_col
)
update
my_schema.child_table
set
col_1 = 'c'
where
parent_id = (select id_col from __parent)
The same thing can be done for insert and delete statements.
Do note you actually need to select from the CTE in the following query, otherwise the statement within will not be executed.

Related

JSON - Selecting a value using operators?

So I'm working with a dataset that looks like below:
Query:
Select Key, NativeObject
FROM dbo.UserResource
Sample Result:
Key | NativeObject
1121 |{ "NativeID": "3452", "AccountType": "Active", "AdNumber": "", "AdditionalDimensions": "", "UserDescription": "User", "IsDeleted": "0" }
A few scenarios I'm encountering issues are:
I want to pull the keys where AdNumber is not null
I want to pull the NativeIDs where value is greater than X number ie) NativeID >= 3000
Currently, I'm working with adding this to the Select clause, but I can't figure out how to add parameters like above. Any help would be appreciated!
json_value('{"IsDeleted": "1"}', '$.IsDeleted') as 'Output'
I created a table and inserted values into that using below code:
CREATE TABLE UserResources( KeyId INT ,NativeObject
NVARCHAR(4000)CONSTRAINT PK_Car PRIMARY KEY(keyID),
CONSTRAINT IsValidJSON1 CHECK (ISJSON(NativeObject) = 1));
insert into UserResources(KeyId,NativeObject) VALUES(4,'{"NativeID": "3452", "AccountType": "Active", "AdNumber": "", "AdditionalDimensions": [{"right":1}], "UserDescription": "User", "IsDeleted": "0" }'),
(2,'{"NativeID": "3452", "AccountType": "Active", "AdNumber": "", "AdditionalDimensions": [{"left":2}], "UserDescription": "User", "IsDeleted": "1" }'),
(3,'{"NativeID": "3452", "AccountType": "Active", "AdNumber": "", "AdditionalDimensions": [{"left":3}], "UserDescription": "User", "IsDeleted": "0" }');
I selected values from JSON column using below code:
SELECT KeyId, NativeObject = JSON_QUERY(NativeObject, '$.AdditionalDimensions')
FROM dbo.UserResources;
I got below result:
In this way you can select values of JSON.

fetch comments reply with limit

comments_table
create table "Comments"
(
comment_id serial
primary key,
post_id integer
references "Posts"
on update cascade on delete cascade,
user_id integer
references "Users"
on update cascade on delete cascade,
comment text,
"comment_likeCount" integer,
comment_pinned boolean,
comment_approved boolean,
comment_parent integer,
"commentCreatedAt" timestamp with time zone not null,
"commnetUpdatedAt" timestamp with time zone not null
);
query
with recursive comments_cte as (
select comment_parent,
coalesce(array_agg(comment_id), array []::integer[]) as replies
from "Comments"
-- where comment_parent in (11,13,1)
group by comment_parent )
select *, array_length(replies, 1) as repliesCount
from "Comments" c
left join comments_cte on (c.comment_id = comments_cte.comment_parent)
where c.comment_parent is null;
output
[
{
"comment_id": 20,
"post_id": 1,
"user_id": 23,
"comment": "second root comment",
"comment_likeCount": 0,
"comment_pinned": true,
"comment_approved": true,
"comment_parent": 20,
"commentCreatedAt": "2022-07-22 21:44:00.526000 +00:00",
"commnetUpdatedAt": "2022-07-22 21:44:01.374000 +00:00",
"replies": [22, 23],
"repliescount": 2
},
{
"comment_id": 1,
"post_id": 1,
"user_id": 23,
"comment": "this a test comment",
"comment_likeCount": 0,
"comment_pinned": true,
"comment_approved": false,
"comment_parent": 1,
"commentCreatedAt": "2022-07-13 16:09:42.909000 +00:00",
"commnetUpdatedAt": "2022-07-13 16:09:42.909000 +00:00",
"replies": [11, 15, 17],
"repliescount": 3
}
]
my query is that how can i limit the replies.
For Example: suppose my database has 100 root comments and has more that 2000 replies in each root comment then it will be really expensive query. If i am able to limit/paginate the number of replies it would really improve the load.
Like YouTube in which you fetch root comments and each root comment has a button to load replies button, which load the replies but not all of them at once.

Postgres SQL Query that returns JSON Object with IDs as dynamic keys

I want to create a (postgres) SQL query that returns a (JSON) object with dynamic keys. Therefore I have created this example tables with some values.
CREATE TABLE foods (
id SERIAL PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE nutrients (
id SERIAL PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE foods_nutrients (
food_id int REFERENCES foods(id) ON UPDATE CASCADE ON DELETE CASCADE,
nutrient_id int REFERENCES nutrients(id) ON UPDATE CASCADE ON DELETE CASCADE,
amount DECIMAL NOT NULL,
CONSTRAINT food_nutrient_pk PRIMARY KEY (food_id, nutrient_id)
);
INSERT INTO foods(name)
VALUES ('Apple'),
('Banana');
INSERT INTO nutrients(name)
VALUES ('Carbohydrates'),
('Protein'),
('Fat');
INSERT INTO foods_nutrients(food_id, nutrient_id, amount)
VALUES (1, 1, 14.0),
(1, 2, 0.3),
(1, 3, 0.2),
(2, 1, 23.7),
(2, 2, 1.1);
The result of the query should look like this JSON if possible. Or at least close enough. The main problem is the object with the IDs of the nutrients as keys, I think.
[
{
"id": 1,
"name": "Apple",
"nutrients": {
"1": 14.0,
"2": 0.3,
"3": 0.2
}
},
{
"id": 2,
"name": "Banana",
"nutrients": {
"1": 23.7,
"2": 1.1
}
}
]
Principally JSON_BUILD_OBJECT() function is needed to construct a combination between id, name and nutrients values. But nutrients requires one more operation which will be using JSON_OBJECT_AGG() in order to get a more complicated object. So, consider using
WITH fn1 AS
(
SELECT fn.food_id, f.name,
JSON_OBJECT_AGG( nutrient_id, amount ) AS nutrients
FROM foods_nutrients fn
JOIN foods f
ON fn.food_id=f.id
JOIN nutrients n
ON fn.nutrient_id=n.id
GROUP BY fn.food_id, f.name
)
SELECT JSON_AGG(
JSON_BUILD_OBJECT( 'id', food_id,
'name', name,
'nutrients', nutrients)
) AS js
FROM fn1
Demo
Btw, using JSONB equivalents of those functions along with JSONB_PRETTY() which nests our current result will yield a nice design as Demonstrated, just like the format within the question :
[
{
"id": 1,
"name": "Apple",
"nutrients": {
"1": 14.0,
"2": 0.3,
"3": 0.2
}
},
{
"id": 2,
"name": "Banana",
"nutrients": {
"1": 23.7,
"2": 1.1
}
}
]
You can try the below - DEMO Here
select row_to_json(fv)
from (
select food_id,f.name,json_agg(
json_build_object(nutrient_id ,amount)
)as nutrients
from foods_nutrients fn join foods f on fn.food_id=f.id
join nutrients n on fn.nutrient_id=n.id group by food_id,f.name
) fv

SQL Database how to link primary and foreign key before inserting

Designing my first SQL database with Postgresql.
Here is my design so far:
This is how the Data comes to the backend :
"questions": [
{
"name": "What is Jhons's last name?",
"category": "Personal",
"explanation": "Just the way it is",
"options": [
{
"name": "James",
"correct": false
},
{
"name": "Ron",
"correct": true
},
{
"name": "Chris",
"correct": false
},
{
"name": "Tom",
"correct": false
}
]
},
{
"name": "What is John's first name?",
"category": "Personal",
"explanation": "Just the way it is 2",
"options": [
{
"name": "John",
"correct": true
},
{
"name": "Rohn",
"correct": false
},
{
"name": "Heimer",
"correct": false
},
{
"name": "Eric",
"correct": false
}
]
}
]
}
What i am trying to do:
I want to insert each Question into it's Questions table.
Then I want to insert each Options object into it's Options Table.
I want to be able to link each option to each questionId primary key (like shown in the picture).
My issue/question:
How can I get the Question ID before inserting, to be able to use that is the options foreign key. It is my understanding that the ID is only created once the row is inserted. Do I need to create my own ID for the question, so i can insert the Options with that created ID?
Thank you for your time
You need to insert the question first before you can insert options. One method is to use CTEs:
with q as (
insert into questions ( . . . )
values ( . . . )
returning *
)
insert into questionoptions (questionid, . . . )
select q.questionid, . . .
from q;
Note two changes that I made to the naming. The options table is called questionoptions because the options are for a question. Also, the primary key and the foreign key have the same name -- questionid -- so the code is self documentating.
Assuming that table Questions is automatically populating id column with a serial number:
DECLARE _question_id integer;
...
INSERT INTO Questions (...) VALUES (...) RETURNING id INTO _question_id;
...
INSERT INTO Options (..., questionId) VALUES (..., _question_id);
More info about RETURNING clause: https://www.postgresql.org/docs/12/sql-insert.html
If the ID is generated through an identity (or serial) column, then you can use lastval():
insert into questions (...) values (...);
insert into options(question_id, ...) values (lastval(), ...);

MSSQL Query JSON displays Null value

I have a table PublicRelations with a column called Students in a SQL Server database called Subjects.
[
{ "Label": "Name", "ColumnValue": "Trudie" },
{ "Label": "Class", "ColumnValue": "PublicRelations" },
{ "Label": "Room", "ColumnValue": "8049" },
{ "Label": "HttpPath", "ColumnValue": "https://www.google.com/" }
]
I only get NULL when I run the below query using the Json_value. I'd like to get it to display the value from the array. I believe this may have to do with the 4000 character limit?
SELECT [StuduentID],
[Students],
--JSON_VALUE([Students],'$.ColumnValue') AS Name --Only returns NULL
FROM [Subjects].[dbo].[PublicRelations] c
CROSS APPLY OPENJSON(c.Students)
WITH ( Name int '$.Name',
Value nvarchar(255) '$.ColmunValue'
) AS jsonValues
WHERE jsonValues.ColumnValue = 'Trudie'
The query works and I can find what I need, but again, I only get NULL when I want to display that part of the JSON column in my results.
The statement is wrong and you has the following issues (as #MartinSmith already mentioned):
Syntax error - '$.ColmunValue' should be '$.ColumnValue'.
Wrong schema definition (the WITH clause) - I can't see Name key in the input JSON.
Wrong use of JSON_VALUE() - this function extracts scalar value from a JSON string, so JSON_VALUE([Students],'$.ColumnValue') returns NULL with this JSON input in lax mode.
You may try with the following statement (based on the statement in the question):
Table:
CREATE TABLE PublicRelations (
StudentID int,
Students nvarchar(1000))
INSERT INTO PublicRelations (StudentID, Students)
VALUES (1, N'[
{ "Label": "Name", "ColumnValue": "Trudie" },
{ "Label": "Class", "ColumnValue": "PublicRelations" },
{ "Label": "Room", "ColumnValue": "8049" },
{ "Label": "HttpPath", "ColumnValue": "https://www.google.com/" }
]')
Statement:
SELECT p.StudentID, j.*
FROM [PublicRelations] p
CROSS APPLY OPENJSON(p.Students) WITH (
Name nvarchar(50) '$.Label',
Value nvarchar(255) '$.ColumnValue'
) j
WHERE EXISTS (
SELECT 1
FROM OPENJSON(p.Students) WITH (Value nvarchar(255) '$.ColumnValue')
WHERE Value = N'Trudie'
) AND (j.Name IN ('Name', 'Class', 'Room'))
Result:
StudentID Name Value
1 Name Trudie
1 Class PublicRelations
1 Room 8049