Get user from table based on id - sql

I have these Postgres tables:
create table deals_new
(
id bigserial primary key,
slip_id text,
deal_type integer,
timestamp timestamp,
employee_id bigint
constraint employee_id_fk
references common.employees
);
create table twap
(
id bigserial primary key,
deal_id varchar not null,
employee_id bigint
constraint fk_twap__employee_id
references common.employees,
status integer
);
create table employees
(
id bigint primary key,
account_id integer,
first_name varchar(150),
last_name varchar(150)
);
New table to query:
create table accounts
(
id bigint primary key,
account_name varchar(150) not null
);
I use this SQL query:
select d.*, t.id as twap_id
from common.deals_new d
left outer join common.twap t on
t.deal_id = d.slip_id and
d.timestamp between '11-11-2021' AND '11-11-2021' and
d.deal_type in (1, 2) and
d.quote_id is null
where d.employee_id is not null
order by d.timestamp desc, d.id
offset 10
limit 10;
How I can extend this SQL query to search also in table employees by account_id and map the result in table accounts by id? I would like to print also accounts. account_name based on employees .account_id.

You need two joins to to make this work for you. One join to get to the employee table, and one more join to get to the accounts table.
select d.*, t.id as twap_id, a.account_name
from common.deals_new d
left outer join common.twap t on
t.deal_id = d.slip_id and
d.timestamp between '11-11-2021' AND '11-11-2021' and
d.deal_type in (1, 2) and
d.quote_id is null
join employees as e on d.employee_id = e.id
join accounts as a on a.id = e.account_id
where d.employee_id is not null
order by d.timestamp desc, d.id
offset 10
limit 10;
Note: I did not fiddle this one, so could have a typo, but I think you get the idea here.

Related

Add join to display items based on key

I have these Postgres tables:
create table employees
(
id bigint primary key,
account_id number,
first_name varchar(150),
last_name varchar(150)
);
create table accounts
(
id bigint primary key,
account_name varchar(150) not null
);
I need to search in table employees by account_id and print result the rows which match in table accounts.id. How I can do this using JOIN?
I'm pretty sure this is what you're looking for.
SELECT a.id, a.account_name, e.first_name, e.last_name
FROM employees as e
JOIN accounts as a on a.id = e.account_id
WHERE e.account_id = 3
This will allow you to search for specific account IDs in the employee table and bring back their corresponding account table information.
You can check this with my dbfiddle here - https://www.db-fiddle.com/f/pwzwQTsHuP27UDF17eAQy4/0

PostgreSQL query for Library Management System

Get the member ID and name of the members to whom no more books can be issued, because they have already got as many books issued as the number for which they are entitled
Following are the schemas:
Book_Records(accession_no,isbn_no)
Book(isbn_no, author, publisher, price)
Members(member_id, member_name,max_no_books,max_no_days)
Book_Issue(member_id,accession_no,issue_date,return_date)
CREATE TABLE BOOK (ISBN_NO VARCHAR(35) PRIMARY KEY,
AUTHOR VARCHAR(35) NOT NULL,
PUBLISHER VARCHAR(35) NOT NULL,
PRICE NUMERIC(10,3));
CREATE TABLE BOOK_RECORDS(ACCESSION_NO VARCHAR(35) PRIMARY KEY,
ISBN_NO VARCHAR(35) REFERENCES BOOK(ISBN_NO));
CREATE TABLE MEMBERS(MEMBER_ID VARCHAR(35) PRIMARY KEY,
MEMBER_NAME VARCHAR(35) NOT NULL,
MAX_NO_BOOKS INT,
MAX_NO_DAYS INT);
CREATE TABLE BOOK_ISSUE(MEMBER_ID VARCHAR(35) REFERENCES MEMBERS(MEMBER_ID),
ACCESSION_NO VARCHAR(35) REFERENCES
BOOK_RECORDS(ACCESSION_NO),
ISSUE_DATE DATE NOT NULL,
RETURN_DATE DATE,
PRIMARY KEY(MEMBER_ID,ACCESSION_NO));
I tried the following query but fails.
SELECT DISTINCT member_name
FROM members AS m
JOIN (
SELECT member_id, COUNT(*) AS no_books_issued
FROM book_issue
GROUP BY member_id,accesion_no
HAVING no_books_issued >= max_no_books
) AS b ON m.member_id = b.member_id;
Presumably, a query like this gets the number of books currently issued:
SELECT member_id, COUNT(*) AS num_books
FROM book_issue
WHERE return_date IS NULL
GROUP BY member_id;
My understanding of the maximum number of books would be concurrently -- that is, only count books that have not been returned. Perhaps you have a different definition.
Then, you can use this in a JOIN, doing the comparison on the maximum outside the subquery:
SELECT member_name
FROM members m JOIN
(SELECT member_id, COUNT(*) AS num_books
FROM book_issue
WHERE return_date IS NULL
GROUP BY member_id
) b
ON b.member_id = m.member_id AND
b.num_books >= m.max_no_books;
Notes:
In a JOIN, the comparison to the outer table needs to be outside the subqueries.
No SELECT DISTINCT is needed.
The GROUP BY for counting books should be only at the member level.

SQL issue - type of fkey

I'm using PostgreSQL
What I need
In SELECT query I need to select owner_type (client or domain). If solution does not exist please help me to rework this schema.
Schema (tables)
Albums - id | client_id (fkey) | domain_id (fkey) | name
Clients - id | first_name | last_name
Domains - id | name
Description: Albums owner can be Client or Domain or future other Nodes...
1. CREATE TABLE QUERY
CREATE TABLE albums
(
id BIGSERIAL PRIMARY KEY,
client_id BIGINT,
domain_id BIGINT,
name VARCHAR(255) NOT NULL,
FOREIGN KEY (client_id) REFERENCES clients(id),
FOREIGN KEY (domain_id) REFERENCES domains(id),
CHECK ((client_id IS NULL) <> (domain_id IS NULL))
);
2. SELECT QUERY
SELECT albums.id,
albums.name,
COALESCE(c.id, d.id) AS owner_id
FROM albums
LEFT JOIN clients c
ON albums.client_id = c.id
LEFT JOIN domains d
ON albums.domain_id = d.id
Need something like -> if c.id === null -> owner_type = 'Domain'
You would seem to want:
SELECT a.id, a.name,
COALESCE(c.id, d.id) AS owner_id,
(CASE WHEN c.id IS NOT NULL THEN 'client' ELSE 'domain' END) as owner_type
FROM albums a LEFT JOIN
clients c
ON a.client_id = c.id LEFT JOIN
domains d
ON a.domain_id = d.id ;
Do you need two separate columns representing client_id and domain_id for the type of owners? It seems that if you were to add more nodes, you would have to add additional columns.
Could you have an owners table representing all types of owners, and have an owner_id foreign key on the albums table?
I was thinking something like this:
CREATE TABLE albums (
id BIGSERIAL PRIMARY KEY,
owner_id BIGINT,
name VARCHAR(255) NOT NULL,
FOREIGN KEY (owner_id) REFERENCES owners(id)
);
CREATE TABLE owners (
id BIGSERIAL PRIMARY KEY,
type VARCHAR(100) NOT NULL,
name VARCHAR(255) NOT NULL
);
You could then query for albums belonging to clients:
SELECT a.id, a.name, o.name AS owner_name
FROM albums a
JOIN owners o ON o.id = a.owner_id
WHERE o.type = 'Client';
As new nodes (types of owners) are added, you simply need to add them to the owners table without modifying the schema of the albums table.
Hope this helps.

Multiple selects on joined tables with group by?

I have three tables with the structures outlined below:
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE
);
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id) NOT NULL,
category BIGINT REFERENCES categories(id) NOT NULL,
text TEXT NOT NULL
);
CREATE TABLE posts_votes (
user_id BIGINT REFERENCES users(id) NOT NULL,
post_id BIGINT REFERENCES posts(id) NOT NULL
value SMALLINT NOT NULL,
PRIMARY KEY(user_id, post_id)
);
I was able to compose a query that gets each post with its user and its total value using the below query:
SELECT p.id, p.text, u.username, COALESCE(SUM(v.value), 0) AS vote_value
FROM posts p
LEFT JOIN posts_votes v ON p.id=t.post_id
JOIN users u ON p.user_id=u.id
WHERE posts.category=1337
GROUP BY p.id, p.text, u.username
But now I want to also return a column that returns the result of SELECT COALESCE((SELECT value FROM posts_votes WHERE user_id=1234 AND post_id=n), 0) for each post_id n in the above query. What would be the best way to do this?
I think an additional LEFT JOIN is a reasonable approach:
SELECT p.id, p.text, u.username, COALESCE(SUM(v.value), 0) AS vote_value,
COALESCE(pv.value, 0)
FROM posts p JOIN
users u
ON p.user_id=u.id LEFT JOIN
topics_votes v
ON p.id = t.post_id LEFT JOIN
post_votes pv
ON pv.user_id = 1234 AND pv.post_id = p.id
WHERE p.category = 1337
GROUP BY p.id, p.text, u.username, pv.value;

Optimise many-to-many join

I have three tables: groups and people and groups_people which forms a many-to-many relationship between groups and people.
Schema:
CREATE TABLE groups (
id SERIAL PRIMARY KEY,
name TEXT
);
CREATE TABLE people (
id SERIAL PRIMARY KEY,
name TEXT,
join_date TIMESTAMP
);
CREATE TABLE groups_people (
group_id INT REFERENCES groups(id),
person_id INT REFERENCES people(id)
);
When I want to query for the latest 10 people who recenlty joined the group which has id = 1:
WITH person_ids AS (SELECT person_id FROM groups_people WHERE group_id = 1)
SELECT * FROM people WHERE id = ANY(SELECT person_id FROM person_ids)
ORDER BY join_date DESC LIMIT 10;
The query needs to scan all of the joined people then ordering them before selecting. That would be slow if the group containing too many people.
Is there anyway to work around it?
Schema (re-)design to allow same person joining multiple group
Since you mentioned that the relationship between groups and people
is many-to-many, I think you may want to move join_date to groups_people
(from people) because the same person can join different groups and each
such event has its own join_date
So I would change the schema to
CREATE TABLE people (
id SERIAL PRIMARY KEY,
name TEXT --, -- change
-- join_date TIMESTAMP -- delete
);
CREATE TABLE groups_people (
group_id INT REFERENCES groups(id),
person_id INT REFERENCES people(id), -- change
join_date TIMESTAMP -- add
);
Query
select
p.id
, p.name
, gp.join_date
from
people as p
, groups_people as gp
where
p.id = gp.person_id
and gp.group_id=1
order by gp.join_date desc
limit 10
Disclaimer: The above query is in MySQL syntax (the question was originally tagged with MySQL)
This seems much easier to write as a simple join with order by and limit:
select p.*
from people p join
groups_people gp
on p.id = gp.person_id
where gp.group_id = 1
order by gp.join_date desc
limit 10; -- or fetch first 10 rows only
Try rewriting using EXISTS
SELECT *
FROM people p
WHERE EXISTS (SELECT 1
FROM groups_people ps
WHERE p.id = ps.person_id and group_id = 1)
ORDER BY join_date DESC
LIMIT 10;