How can I select data from 3 tables in sql? - sql

I have 3 tables:
create table user (
user_id integer primary key autoincrement,
username string not null,
email string not null,
pw_hash string not null
);
create table product (
product_id integer primary key autoincrement,
productname string not null,
productdescription string not null,
);
create product_review (
product_id integer,
user_id integer,
review,
);
Now, I want to display all the reviews from user_id=1. A simple query for this would be
select * from product_review where user_id = 1
However, I want the data listed as -->
username productname review
John iPad3 Super awesome
John SonyVaio Even more awesome

Try this:
SELECT username, productname, review
FROM user a INNER JOIN product_review pr
ON a.user_id = pr.user_id INNER JOIN product p
ON p.product_id = pr.product_id
WHERE a.user_id = 1

Related

How to do an INNER JOIN without getting duplicate rows?

I have two tables. A jobs table (with job.user_id) and a users table with user.id
I am trying to select all jobs and include username from users table, but I'm getting double the number of results I should be:
async getAll(context: any) {
const all = await db.queryEntries(`
SELECT j.*, u.username FROM jobs as j, users as u
INNER JOIN jobs ON jobs.user_id = u.id ORDER BY j.created_at DESC
`);
context.response.body = all;
}
I get two of everything. How do I just add 'username' where job.user_id = user.id?
users:
CREATE TABLE users (
id string primary key,
email string unique,
hashed_password string,
created_at TEXT,
updated_at TEXT,
username TEXT,
contactme INTEGER DEFAULT 1,
phone TEXT
);
jobs:
CREATE TABLE jobs (
id STRING PRIMARY KEY,
user_id STRING NOT NULL,
title STRING NOT NULL,
description STRING NOT NULL,
pay STRING NOT NULL,
type STRING NOT NULL,
created_at TEXT,
updated_at TEXT,
contact TEXT
);
I had to remove the "FROM users":
SELECT j.*, u.username FROM jobs as j
INNER JOIN users u ON j.user_id = u.id ORDER BY j.created_at DESC
This produces only one result now.

Sum two SQLite columns, when they're subqueries

I have a table of receipts. Each one is associated with a service, and each person is obligated to pay equally for it, except when they are assigned an extra fee that can be activated/deactivaded (0/1). So I used a subquery to get the extra amount they have to pay only if that fee is active; the table 'fees' contains the user_id, the service_id, the extra amount and the active flag. And then, I should get the total per person, adding the extra fee (if any) to the subtotal (receipt total amount minus any active extra fee, and then divided by the number of persons who are obligated to contribute).
SELECT
P.nombre AS person,
S.nombre AS service,
(
SELECT TOTAL(C.value)
FROM fees C
WHERE C.user_id = P.id AND C.service_id = O.service_id AND C.active = 0
) AS fee,
IFNULL(NULL, 23333) AS subtotal,
(fee + subtotal) as total
FROM receipts R
LEFT JOIN obligations O ON O.service_id = R.service_id
LEFT JOIN persons P ON O.user_id = P.id
LEFT JOIN services S ON O.service_id = S.id
WHERE R.id = 3 AND O.active = 0;
Note: 23333 (the subtotal) will be replaced with a '?' and then I'll pass as argument to execute the query with Golang that result that I've already got from another function
Problem occurs at this line
(fee + subtotal) as total
Output: no such column: fee
If I run the query without that line, it will actually return a table with the active extra fees and subtotal, but I'm stuck when trying to create a final column to add those two values.
Thanks!
Edit
Following Stefan's advice, here are the statements I used to create the tables:
CREATE TABLE IF NOT EXISTS persons (id INTEGER PRIMARY KEY, name TEXT NOT NULL, active INTEGER DEFAULT 0); CREATE UNIQUE INDEX per_nom_uindex on persons (name)
CREATE TABLE IF NOT EXISTS services (id INTEGER PRIMARY KEY, name TEXT NOT NULL, active INTEGER DEFAULT 0); CREATE UNIQUE INDEX ser_nom_uindex on services (name)
CREATE TABLE IF NOT EXISTS receipts (id INTEGER PRIMARY KEY, y INTEGER NOT NULL, m INTEGER NOT NULL, service_id INTEGER NOT NULL, amount INTEGER NOT NULL, FOREIGN KEY (service_id) REFERENCES services (id))
CREATE TABLE IF NOT EXISTS fees (id INTEGER PRIMARY KEY, person_id INTEGER NOT NULL, service_id INTEGER NOT NULL, amount INTEGER NOT NULL, active INTEGER DEFAULT 0, FOREIGN KEY(person_id) REFERENCES persons(id), FOREIGN KEY(service_id) REFERENCES services(id))
CREATE TABLE IF NOT EXISTS obligations (id INTEGER PRIMARY KEY, person_id INTEGER NOT NULL, service_id INTEGER NOT NULL, active INTEGER DEFAULT 0, FOREIGN KEY(person_id) REFERENCES persons(id), FOREIGN KEY(service_id) REFERENCES services(id))
Consider moving the subquery from SELECT to JOIN clause (often called derived table) and adjust it with GROUP BY aggregation on user_id and service_id. Doing so, this allows you to reference the column as needed and even avoid rowwise aggregation (unless the SQLite engine runs it as a single aggregation under the hood).
SELECT
P.nombre AS person,
S.nombre AS service,
C.fee, -- REFERENCE SUBQUERY COLUMN
IFNULL(?, 23333) AS subtotal,
C.fee + IFNULL(?, 23333) as total -- REPEAT NEEDED EXPRESSION
FROM receipts R
LEFT JOIN obligations O
ON O.service_id = R.service_id
LEFT JOIN persons P
ON O.user_id = P.id
AND O.active = 0 -- MOVED FROM WHERE CLAUSE
LEFT JOIN services S
ON O.service_id = S.id
LEFT JOIN (
SELECT user_id,
service_id,
TOTAL(value) AS fee
FROM fees
WHERE active = 0
GROUP BY user_id,
service_id
) C ON C.user_id = P.id
AND C.service_id = O.service_id
WHERE R.id = 3

SQL Join Table as JSON data

I am trying to join reviews and likes onto products, but it seems, for some reason that the output of "reviews" column is duplicated by the length of another foreign table, likes, the output length of "reviews" is
amount of likes * amount of reviews
I have no idea why this is happening
My desired output is that the "reviews" column contains an array of JSON data such that one array is equal to one row of a related review
Products
Title Image
----------------------
Photo photo.jpg
Book book.jpg
Table table.jpg
Users
Username
--------
Admin
John
Jane
Product Likes
product_id user_id
---------------------
1 1
1 2
2 1
2 3
Product Reviews
product_id user_id review
-------------------------------------
1 1 Great Product!
1 2 Looks Great
2 1 Could be better
This is the query
SELECT "products".*,
array_to_json(array_agg("product_review".*)) as reviews,
EXISTS(SELECT * FROM product_like lk
JOIN users u ON u.id = "lk"."user_id" WHERE u.id = 4
AND "lk"."product_id" = products.id) AS liked,
COUNT("product_like"."product_id") AS totalLikes from "products"
LEFT JOIN "product_review" on "product_review"."product_id" = "products"."id"
LEFT JOIN "product_like" on "product_like"."product_id" = "products"."id"
group by "products"."id"
Query to create schema and insert data
CREATE TABLE products
(id SERIAL, title varchar(50), image varchar(50), PRIMARY KEY(id))
;
CREATE TABLE users
(id SERIAL, username varchar(50), PRIMARY KEY(id))
;
INSERT INTO products
(title,image)
VALUES
('Photo', 'photo.jpg'),
('Book', 'book.jpg'),
('Table', 'table.jpg')
;
INSERT INTO users
(username)
VALUES
('Admin'),
('John'),
('Jane')
;
CREATE TABLE product_review
(id SERIAL, product_id int NOT NULL, user_id int NOT NULL, review varchar(50), PRIMARY KEY(id), FOREIGN KEY (product_id) references products, FOREIGN KEY (user_id) references users)
;
INSERT INTO product_review
(product_id, user_id, review)
VALUES
(1, 1, 'Great Product!'),
(1, 2, 'Looks Great'),
(2, 1, 'Could be better')
;
CREATE TABLE product_like
(id SERIAL, product_id int NOT NULL, user_id int NOT NULL, PRIMARY KEY(id), FOREIGN KEY (product_id) references products, FOREIGN KEY (user_id) references users)
;
INSERT INTO product_like
(product_id, user_id)
VALUES
(1, 1),
(1, 2),
(2, 1),
(2, 3)
fiddle with the schema and query:
http://sqlfiddle.com/#!15/dff2c/1
Thanks in advance
The reason you are getting multiple results is because of the one-to-many relationships between product_id and product_review and product_like causing duplication of rows prior to aggregation. To work around that, you need to perform the aggregation of those tables in subqueries and join the derived tables instead:
SELECT "products".*,
"pr"."reviews",
EXISTS(SELECT * FROM product_like lk
JOIN users u ON u.id = "lk"."user_id" WHERE u.id = 4
AND "lk"."product_id" = products.id) AS liked,
COALESCE("pl"."totalLikes", 0) AS totalLikes
FROM "products"
LEFT JOIN (SELECT product_id, array_to_json(array_agg("product_review".*)) AS reviews
FROM "product_review"
GROUP BY product_id) "pr" on "pr"."product_id" = "products"."id"
LEFT JOIN (SELECT product_id, COUNT(*) AS "totalLikes"
FROM "product_like"
GROUP BY product_id) "pl" on "pl"."product_id" = "products"."id"
Output:
id title image reviews liked totallikes
1 Photo photo.jpg [{"id":1,"product_id":1,"user_id":1,"review":"Great Product!"},{"id":2,"product_id":1,"user_id":2,"review":"Looks Great"}] f 2
2 Book book.jpg [{"id":3,"product_id":2,"user_id":1,"review":"Could be better"}] f 2
3 Table table.jpg f 0
Demo on dbfiddle

Select a product that is on all interventions

Hello my question is simple for some of yours ^^
I've a table product, reference, and intervention. When there is an intervention the table reference make the link between products that we need for the interventions and the intervention.
I would like to know how to do to search products that have made part of all interventions.
This are my tables :
--TABLE products
create table products (
reference char(5) not null check ( reference like 'DT___'),
designation char(50) not null,
price numeric (9,2) not null,
primary key(reference) );
-- TABLE interventions
create table interventions (
nointerv integer not null ,
dateinterv date not null,
nameresponsable char(30) not null,
nameinterv char(30) not null,
time float not null check ( temps !=0 AND temps between 0 and 8),
nocustomers integer not null ,
nofact integer not null ,
primary key( nointerv),
foreign key( noclient) references customers,
foreign key (nofacture) references facts
);
-- TABLE replacements
create table replacements (
reference char(5) not null check ( reference like 'DT%'),
nointerv integer not null,
qtereplaced smallint,
primary key ( reference, nointerv ),
foreign key (reference) references products,
foreign key(nointerv) references interventions(nointerv)
);
--EDIT :
This is a select from my replacement table
We can see in this picture that the product DT802 is used in every interventions
Thanks ;)
This will show 1 line intervention - products. Is this you are expecting for?
select interventions.nointerv, products.reference
from interventions
inner join replacements on interventions.nointerv = replacements.nointerv
inner join products on replacements.reference = products.reference;
This one?
select products.reference, products.designation
from interventions
inner join replacements on interventions.nointerv = replacements.nointerv
inner join products on replacements.reference = products.reference
group by products.reference, products.designation
having count(*) = (select count(*) from interventions);
Your question is hard to follow. If I interpret it as all nointerv in replacements whose reference contains all products, then:
select nointerv
from replacements r
group by nointerv
having count(distinct reference) = (select count(*) from products);

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.