How to request JSON among three tables - sql

Suppose I have 3 tables employee, child and employee_child.
The first table contains id and name columns, the second contains id and name as well, and in the last contains id, employee_id, child_id.
Those tables (employee and child) are associated in the table employee_child.
To help, I let these queries to populate those tables:
CREATE TABLE employee (
id INTEGER PRIMARY KEY,
name VARCHAR(128))
INSERT INTO employee(id, name)
VALUES(1, 'Josh'), (2, 'Michel'), (3, 'Will')
CREATE TABLE child (
id INTEGER PRIMARY KEY,
name VARCHAR(128))
INSERT INTO child(id, name)
VALUES(1, 'Karen'), (2, 'Adam'), (3, 'Ellen')
CREATE TABLE employee_child (
id INTEGER PRIMARY KEY,
employee_id INTEGER NOT NULL,
child_id INTEGER NOT NULL)
ALTER TABLE employee_child ADD FOREIGN KEY (employee_id) REFERENCES employee (id)
ALTER TABLE employee_child ADD FOREIGN KEY (child_id) REFERENCES child (id)
INSERT INTO employee_child(id, employee_id, child_id)
VALUES(1, 1, 1), (2, 1, 2), (3, 2, 3)
Results of selects from tables
I need to get the employee's name and his child's names by employee.id using JSON request, and get the following answer:
{
"id":1,
"name":"Josh",
"children":[
{
"id":1,
"name":"Karen"
},
{
"id":2,
"name":"Adam"
}
]
}
I've tried another way, but the most right answer that I got was this following query:
SELECT [''] = (SELECT e.id, e.name
FROM employee e
WHERE e.id = 1
FOR JSON PATH),
[children] = (SELECT c.id, c.name
FROM child c
INNER JOIN employee_child ec ON ec.child_id= c.id
WHERE ec.employee_id = 1
FOR JSON PATH)
FOR JSON PATH
I'd like to get a support to achieve the fully answer.

FOR JSON AUTO seems to get you exactly what you want:
SELECT e.id,
e.name,
children.id,
children.name
FROM dbo.employee e
JOIN dbo.employee_child ec ON e.id = ec.employee_id
JOIN dbo.child children ON ec.child_id = children.id
WHERE e.id = 1
FOR JSON AUTO;

Related

Join columns to search string

I have these tables which I would like to query:
create table employees
(
id bigint generated by default as identity (maxvalue 2147483647),
username varchar(100) not null,
password varchar(60) not null,
account_id bigint,
role_id bigint,
first_name varchar(150),
last_name varchar(150),
primary key (id)
);
create table accounts
(
id bigint generated by default as identity,
account_name varchar(150) not null,
account_group_id bigint not null,
primary key (id)
);
Test data:
insert into employees (id, username, password, account_id) values
(1, "test user", "pass", 3),
(2, "test user2 ", "pass", 4);
insert into accounts (id, account_name, account_group_id) values
(1, "main", 3),
(2, "second", 4);
(3, "third", 4);
I need to create a query which searches into table employees by account_name. I tried this:
Example when I send search param second I need to get a row result: test user2
SELECT * FROM common.employees e
WHERE e.??????? iLIKE CONCAT('%', :params, '%')
Do you know how I can join the tables?
You cannot directly parameterize Sql identifier(columns, tables), You can only parameterize values.
Prepared statements can take parameters: values that are substituted into the statement when it is executed.
https://www.postgresql.org/docs/current/sql-prepare.html
In your code. WHERE e.??????? cannot be easily parameterized. You need to use plpgsql functions.
prepare test(text,int) as SELECT e.* FROM employees e
join accounts a on e.account_id = a.id
WHERE a.account_name iLIKE CONCAT('%', $1, '%')
and a.account_group_id = $2;
If your already have test prepare statement in the active session then DEALLOCATE test;
suppose the account_group_id = 1 then:
execute test('third', 1);
Join the 2 tables like that (result here)
SELECT e.* FROM
employees e, accounts a
WHERE
e.account_id = a.id
and a.account_name = 'second'
To include columns account_group_id and account_id into the result you can get as below :
Though e.* will contain all the info that is present in employee table which include account_id as well. So if you want to customized your result set you can do that according to your need:
SELECT e.*,a.account_group_id
FROM employees e
INNER JOIN accounts a ON a.id = e.account_id
WHERE a.account_name = param
If you just use an inner join and join on the account table using the account_id and add a WHERE clause where you only select from employee where the account_name equals your param....which I'm guessing will be a varchar
SELECT e.*, a.account_group_id
FROM employees e
INNER JOIN accounts a ON a.id = e.account_id
WHERE a.account_name = param
or
WHERE a.account_name LIKE '%param%'
but the second may bring back other users as the param could exist in other names.
Also I don't believe the data in your example is correct as surely the account_id would link to the id in the accounts table...so passing second would in fact get you an employee who's account_id is 2.

Get column values from mapping tables "id | value" binding

I am trying to get all the columns associated to with my item, some columns are "key | value" paired and that's where my problem is. My idea for a structure looks like this
I can retrieve 1 item from Posts along with all associated tag names with this query, but the problem is that I just can get 1 post
SELECT TOP(10)
bm.title, bm.post_id,
a.name AS tag1, b.name AS tag2, c.name AS tag3, d.name AS tag4
FROM
Posts AS bm
INNER JOIN
Tagmap AS tm
INNER JOIN
Tag AS a ON a.tag_id = tm.tag_id1
INNER JOIN
Tag AS b ON b.tag_id = tm.tag_id2
INNER JOIN
Tag AS c ON c.tag_id = tm.tag_id3
INNER JOIN
Tag AS d ON d.tag_id = tm.tag_id4
ON bm.post_id = tm.post_id
Here is the DDL for the table, or you can get it from this PasteBin link:
CREATE TABLE Tag
(
tag_id int NOT NULL identity(0,1) primary key,
name nvarchar(30) NOT NULL,
);
CREATE TABLE Tagmap
(
id int NOT NULL identity(0,1) primary key,
post_id int FOREIGN KEY REFERENCES Posts(post_id),
tag_id1 int FOREIGN KEY REFERENCES Tag(tag_id),
tag_id2 int FOREIGN KEY REFERENCES Tag(tag_id),
tag_id3 int FOREIGN KEY REFERENCES Tag(tag_id),
tag_id4 int FOREIGN KEY REFERENCES Tag(tag_id)
);
CREATE TABLE Posts
(
post_id int NOT NULL identity(0,1) primary key,
title nvarchar(50) not null,
);
INSERT INTO Posts VALUES ('Title1');
INSERT INTO Posts VALUES ('Title2');
INSERT INTO Tag VALUES ('Tag number one');
INSERT INTO Tag VALUES ('Tag number two');
INSERT INTO Tag VALUES ('Tag number three');
INSERT INTO Tag VALUES ('Tag number four');
INSERT INTO Tagmap VALUES (0, 0, 1, 2, 3);
My question: is my approach totally off? Should I change the structure or is it good?
If so how can it be better and how can I retrieve all these "key | value" columns along with my posts?
First, you should fix your data structure, so you have one row in tagMap per post_id and tag_id -- not four!
But event with your current structure, I imagine that not all posts have four tags. So, with your current data model you should be using LEFT JOIN, rather than INNER JOIN.

How to join tables together via Ids using SQLite?

I am having trouble joining parts of tables. I want first and last names of the people and whatever their interest is to be joined together. I get this error message: "[1] [SQLITE_ERROR] SQL error or missing database (ambiguous column name: pi.PersonID)"
CREATE TABLE people (
PersonID INTEGER PRIMARY KEY AUTOINCREMENT,
FirstName VARCHAR(100),
LastName VARCHAR(100)
);
INSERT INTO people (FirstName, LastName)
VALUES ('Walter', 'White'),
('Jesse', 'Pinkman'),
('Saul', 'Goodman');
SELECT * FROM people;
CREATE TABLE interests (
InterestID INTEGER PRIMARY KEY AUTOINCREMENT,
Interest VARCHAR(100)
);
INSERT INTO interests (Interest)
values ('Swimming'),
('Basketball'),
('Running');
SELECT * FROM interests;
CREATE TABLE persons_interests (
PersonID INTEGER,
InterestID INTEGER,
PRIMARY KEY (PersonID, InterestID),
FOREIGN KEY (PersonID) REFERENCES people,
FOREIGN KEY (InterestID) REFERENCES interests
);
DROP TABLE persons_interests;
INSERT INTO persons_interests (PersonID, InterestID)
VALUES (1, 3),
(2, 2),
(3, 3);
SELECT * FROM persons_interests;
SELECT FirstName, LastName, Interest FROM people p, interests i
JOIN persons_interests pi on p.PersonID = pi.PersonID
JOIN persons_interests pi on i.Interest = pi.InterestID;
Don't mix implicit an explicit joins! You seem to want:
select p.firstname, p.lastname, i.interest
from people p
inner join persons_interests pi on pi.personid = p.personid
inner join interests i on i.interestid = pi.interestid;
Here, each table appears just once in the from clause, with the relevant join conditions.

Aggregate SQLite query across multiple tables using JSON1

I can't get my head around the following problem. The other day I learned how to use the JSON1 family of functions, but this time it seems to be more of an SQL issue.
This is my database setup:
CREATE TABLE persons(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE)
CREATE TABLE interests(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE)
CREATE TABLE persons_interests(person INTEGER, interest INTEGER, FOREIGN KEY(person) REFERENCES persons(id), FOREIGN KEY(interest) REFERENCES interests(id))
INSERT INTO persons(name) VALUES('John')
INSERT INTO persons(name) VALUES('Jane')
INSERT INTO interests(name) VALUES('Cooking')
INSERT INTO interests(name) VALUES('Gardening')
INSERT INTO interests(name) VALUES('Relaxing')
INSERT INTO persons_interests VALUES(1, 1)
INSERT INTO persons_interests VALUES(1, 2)
INSERT INTO persons_interests VALUES(2, 3)
Based on this data I'd like to get the following output, which is all interests of all persons aggregated into a single JSON array:
[{name: John, interests:[{name: Cooking},{name: Gardening}]}, {name: Jane, interests:[{name: Relaxing}]}]
Now the following is what I tried to do. Needless to say, this doesn't give me what I want:
SELECT p.name, json_object('interests', json_group_array(json_object('name', i.name))) interests
FROM persons p, interests i
JOIN persons_interests pi ON pi.person = p.id AND pi.interest = i.id
The undesired output is:
John|{"interests":[{"name":"Cooking"},{"name":"Gardening"},{"name":"Relaxing"}]}
Any help is highly appreciated!
For using json_group_array you must group line , in your case by person , except you want only one row with all your results .
Example 1)
This first version , will give you 1 json object by person , so the result will be N rows for N persons :
SELECT json_object( 'name ',
p.name,
'interests',
json_group_array(json_object('name', i.name))) jsobjects
FROM persons p, interests i
JOIN persons_interests pi ON pi.person = p.id AND pi.interest = i.id
group by p.id ;
Example 2)
This second version , will give return 1 big json array that contains all persons , but you fetch only one row .
SELECT json_group_array(jsobjects)
FROM (
SELECT json_object( 'name ',
p.name,
'interests',
json_group_array(json_object('name', i.name))) jsobjects
FROM persons p, interests i
JOIN persons_interests pi ON pi.person = p.id AND pi.interest = i.id
group by p.id
) jo ;

Select rows that have a specific set of items associated with them through a junction table

Suppose we have the following schema:
CREATE TABLE customers(
id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE items(
id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE customers_items(
customerid INTEGER,
itemid INTEGER,
FOREIGN KEY(customerid) REFERENCES customers(id),
FOREIGN KEY(itemid) REFERENCES items(id)
);
Now we insert some example data:
INSERT INTO customers(name) VALUES ('John');
INSERT INTO customers(name) VALUES ('Jane');
INSERT INTO items(name) VALUES ('duck');
INSERT INTO items(name) VALUES ('cake');
Let's assume that John and Jane have id's of 1 and 2 and duck and cake also have id's of 1 and 2.
Let's give a duck to John and both a duck and a cake to Jane.
INSERT INTO customers_items(customerid, itemid) VALUES (1, 1);
INSERT INTO customers_items(customerid, itemid) VALUES (2, 1);
INSERT INTO customers_items(customerid, itemid) VALUES (2, 2);
Now, what I want to do is to run two types of queries:
Select names of customers who have BOTH a duck and a cake (should return 'Jane' only).
Select names of customers that have a duck and DON'T have a cake (should return 'John' only).
For the two type of queries listed, you could use the EXISTS clause. Below is an example query using the exists clause:
SELECT cust.name
from customers AS cust
WHERE EXISTS (
SELECT 1
FROM items
INNER JOIN customers_items ON items.id = customers_items.itemid
INNER JOIN customers on customers_items.customerid = cust.id
WHERE items.name = 'duck')
AND NOT EXISTS (
SELECT 1
FROM items
INNER JOIN customers_items ON items.id = customers_items.itemid
INNER JOIN customers on customers_items.customerid = cust.id
WHERE items.name = 'cake')
Here is a working example: http://sqlfiddle.com/#!6/3d362/2