PostgreSQL select query doesn't work - sql

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.

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;

SQL Query to check if a record does not exist in another table

I have a table which holds details of all Students currently enrolled in classes which looks like this:
CREATE TABLE studentInClass(
studentID int,
classID int,
FOREIGN KEY(studentID) references students(studentID),
foreign key(classID) references class(classID)
);
And another table which contains details of students who have paid for classes:
CREATE TABLE fees(
feesID INTEGER PRIMARY KEY AUTOINCREMENT,
StudentID INTEGER,
AmountPaid INT,
Date DATE,
FOREIGN KEY(StudentID) REFERENCES students(StudentID));
What I want to do is check whether a student who is in a class has not paid for that class. I am struggling to write a SQL query which does so. I have tried multiple queries such as:
Select studentInClass.StudentID
from fees, studentInClass
where fees.StudentID = studentInClass.StudentID;
But this returns no data. I'm not sure how to proceed from here. Any help will be appreciated.
You want outer join :
select s.StudentID, (case when f.AmountPaid is not null
then 'Yes'
else 'No'
end) as Is_fees_paid
from studentInClass s left join
fees f
on f.StudentID = s.StudentID;
With NOT EXISTS:
select s.*
from studentInClass s
where not exists (
select 1 from fees
where studentid = s.studentid
)
with this you get all the rows from the table studentInClass for which there is not the studentid in the table fees.
It's not clear if you also need to check the date.
check it please:
select studentInClass.StudentID
from studentInClass inner join fees ON fees.StudentID = studentInClass.StudentID

Appropriate SELECT Statement

I have the following tables:
CREATE TABLE Subject
(
Subject_Code INTEGER,
Subject_Year VARCHAR (8),
PRIMARY KEY (Subject_Code, Subject_Year),
Teacher_ID INTEGER REFERENCES
);
CREATE TABLE Teacher
(
TeacherID INTEGER PRIMARY KEY,
FirstName TEXT,
Department_ID INTEGER References Academic Department(Department_ID)
);
CREATE TABLE Subject-taken
(
Marks_Obtained INTEGER,
Subject_Code INTEGER REFERENCES subject (Subject_Code),
Candidate_ID INTEGER REFERENCES Candidate (Candidate_ID),
PRIMARY KEY (Subject_Code, Candidate_ID)
);
CREATE TABLE Academic_Department
(
Department_ID INTEGER PRIMARY KEY,
Department_Name TEXT
);
I've already tried the following select statement
SELECT m.subject_code,
MIN (marks_obtained) AS Min_Marks,
MAX (marks_obtained) AS Max_Marks
FROM Subject-taken m, Subject a
GROUP BY m.Subject_Code;
Want to use the join function any suggestions on where to use it in order to join the departments with subjects and students
Make use of joins to link your data between tables. Use group by to make statistics by some fields. You can try something like this:
SELECT
Subjects.Subject_Code,
Subjects.Subject_Name,
Teachers.TeacherID,
Academic_Department.Department_ID,
min(Subject-taken.Marks_Obtained) as min_marks,
max(Subject-taken.Marks_Obtained) as max_marks,
avg(Subject-taken.Marks_Obtained) as avg_marks,
stddev_samp(subject-taken.Marks_Obtained) as stddev_marks
FROM
Subjects LEFT JOIN
Teachers ON Subjects.TeacherID = Subjects.TeacherID LEFT JOIN
Academic_Department ON Teachers.Department_ID = Academic_Department.Department_ID LEFT JOIN
Subject-taken ON Subjects.Subject_Code = Subject-taken.Subject_Code
GROUP BY
Subjects.Subject_Code,
Subject.Subject_Name,
Teacher.TeacherID,
Academic_Department.Department_ID
I don't really know if stddev_samp is the aggregate function you need, stddev_pop is also available. Please refer to this PostgreSQL documentation table to find out.

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