Select a product that is on all interventions - sql

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);

Related

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

SQLite: Get Output From Two Tables Using Common Reference ID

I am new in SQLite and i have been working on an issue for quite a long time.
Lets say we have 2 database table say tbl_expense and tbl_category. Please find below the following table structure.
tbl_category
CREATE TABLE IF NOT EXISTS tbl_category(
category_id INTEGER PRIMARY KEY AUTOINCREMENT,
category_name VARCHAR(20) DEFAULT NULL,
category_desc VARCHAR(500) DEFAULT NULL,
category_icon VARCHAR(100) DEFAULT NULL,
category_created timestamp default CURRENT_TIMESTAMP
)
tbl_expense
CREATE TABLE IF NOT EXISTS tbl_expense(
expense_id INTEGER PRIMARY KEY AUTOINCREMENT,
expense_name VARCHAR(20) DEFAULT NULL,
expense_desc VARCHAR(500) DEFAULT NULL,
expense_type VARCHAR(20) DEFAULT NULL,
expense_amt DECIMAL(6.3) DEFAULT NULL,
expense_date TIMESTAMP DEFAULT NULL,
expense_category INTEGER DEFAULT NULL,
expense_created_date timestamp DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (expense_category) REFERENCES tbl_category(category_id)
ON DELETE SET NULL
)
Assume we have data in the tables like this below.
Expected Output:
Assure we have category_id and expense_category as common fields. How can i create an SQL Query where i can list all categories and sum of their expense amount as follows.
Please help me on this issue.
You need an INNER join of the tables and aggregation:
SELECT c.category_name Category,
SUM(e.expense_amt) Amount
FROM tbl_category c INNER JOIN tbl_expense e
ON e.expense_category = c.category_id
GROUP BY c.category_id;
If you want all categories from the table tbl_category, even those that are not present in tbl_expense, use a LEFT join and TOTAL() aggregate function:
SELECT c.category_name Category,
TOTAL(e.expense_amt) Amount
FROM tbl_category c LEFT JOIN tbl_expense e
ON e.expense_category = c.category_id
GROUP BY c.category_id;

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.

NOT EXIST clause

I am trying to find Products that have never been ordered. My 2 tables look like this.
CREATE TABLE Orders
(OrderNum NUMBER(10) NOT NULL,
OrderDate DATE NOT NULL,
Cust NUMBER(10),
Rep NUMBER(10),
Mfr CHAR(3) NOT NULL,
Product CHAR(5) NOT NULL,
Qty NUMBER(5) NOT NULL,
Amount NUMBER(9,2) NOT NULL,
CONSTRAINT OrdersPK
PRIMARY KEY (OrderNum));
CREATE TABLE Products
(Mfr CHAR(3) NOT NULL,
Product CHAR(5) NOT NULL,
Description VARCHAR2(20) NOT NULL,
Price NUMBER(9,2) NOT NULL,
QtyOnHand NUMBER(5),
CONSTRAINT ProductsPK
PRIMARY KEY (Mfr, Product));
The code I currently have looks like this.
SELECT Mfr, Product
FROM Products
WHERE NOT EXISTS (SELECT Products.Mfr
FROM Orders, Products
WHERE Orders.Mfr = Products.Mfr);
Although I am not getting any errors there are also no results showing up.
**EDIT: There are 26 Products and 19 of them have been ordered. I am expecting to get 7 Results but I am getting 0.
You can use NOT EXISTS, but you need to compare both keys:
SELECT p.Mfr, p.Product
FROM Products p
WHERE NOT EXISTS (SELECT 1
FROM Orders o
WHERE o.Mfr = p.Mfr AND
o.Product = p.Product
);
This is a case where it makes lots of sense to have an auto generated primary key that can be used for foreign key relationships.
Try this one
SELECT Mfr, Product
FROM Products
WHERE NOT EXISTS (SELECT Orders.Mfr
FROM Orders
WHERE Orders.Mfr = Products.Mfr AND Orders.Product = Products.Product);
An alternative is to use the set operation operator EXCEPT - as you want "the set of Products that don't exist in Orders":
SELECT
Mfr,
Product
FROM
Products
EXCEPT
SELECT
DISTINCT
Mfr,
Product
FROM
Orders
You can then use this as a subquery to get full product information.
SELECT
*
FROM
Products
INNER JOIN (
SELECT
Mfr,
Product
FROM
Products
EXCEPT
SELECT
DISTINCT
Mfr,
Product
FROM
Orders
) AS ProductsWithNoOrders ON
Products.Mfr = ProductsWithNoOrders.Mfr AND
Products.Product = ProductsWithNoOrders.Product

PostgreSQL select query doesn't work

I have 3 tables:
CREATE table materials
(id serial primary key not null,
name varchar(50) not null,
unit varchar(10) not null default 'шт',
price decimal(12, 2) not null check (price>0));
CREATE table warehouses
(id serial primary key not null,
lastname varchar(25) not null);
CREATE table materials_in_warehouses
(id_warehouses integer references warehouses(id) on update cascade on delete cascade,
id_materials integer references materials(id),
unit varchar(15) default 'шт',
count integer not null CHECK (count>0),
lastdate date not null,
primary key (id_warehouses, id_materials);
And i need to select for each material : name; count of warehouses, where the material is present in an amount of > 100.
I tried to do something like this:
select materials.name, count(select * from materials_in_warehouses where
materials.id = materials_in_warehouses.id_materials AND material_in_warehouses.count > 100) as sount from materials;
But of course, this nonsense can't work.
Thank you in advance.
Pretty straight forward.
SELECT m.name, count(miw.id_warehouses)
FROM materials m
LEFT JOIN materials_in_warehouses miw ON m.id=miw.id_materials AND miw."count">100
GROUP BY m.id, m.name
Your mistake might have just been the fact that you're using count as a column name, when it's an aggregate function. Use double quotes in PostgreSql for that:
Escaping keyword-like column names in Postgres
Try like this
select materials.name, count(
select id_warehouses from materials_in_warehouses join materials
on materials.id = materials_in_warehouses.id_materials
where material_in_warehouses.count > 100
) as sount from materials;
SELECT m.name, COUNT(w.id) AS locs
FROM materials m, warehouses w, materials_in_warehouses mw
WHERE m.id = mw.id_materials
AND w.id = mw.id_warehouses
AND mw.count > 100
GROUP BY m.name;
If that works I'll come back and explain how I came up with it.