Sum two SQLite columns, when they're subqueries - sql

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

Related

How to assign a value of 0 if a row doesn't meet the requirements

I'm trying to return an actor name field (A_Name) and the movie role field (MR_Role) with the number of times each actor has an "Uncredited" in their role. I also want to return a 0 if that specific actor does not have an "Uncredited" in their role. You'll see in my table formats there's another table, but I don't need to use it to run this query since that just has movie id and year of release.
I've used this code to return the amount of uncredited roles for each actor, but without the 0's:
SELECT A_NAME, COUNT(MR_ROLE)
FROM ACTOR JOIN MOVIEROLE ON ACTOR.A_ID = MOVIEROLE.A_ID
WHERE MOVIEROLE.MR_ROLE LIKE '%UNCREDITED%'
GROUP BY A_NAME;
Here are the results from the above query
These are the formats of the tables I'm using:
ACTOR TABLE:
(A_ID INTEGER NOT NULL,
A_NAME CHAR(25) NOT NULL,
A_YOB INTEGER NOT NULL,)
MOVIEROLE TABLE:
(MR_ID INTEGER NOT NULL,
A_ID INTEGER NOT NULL,
M_ID INTEGER NOT NULL,
MR_ROLE CHAR(45),
PRIMARY KEY (MR_ID),
FOREIGN KEY(A_ID) REFERENCES ACTOR,
FOREIGN KEY(M_ID) REFERENCES MOVIE)
You need a LEFT JOIN to return the 0s:
SELECT a.A_NAME, COUNT(mr.MR_ROLE)
FROM ACTOR a LEFT JOIN
MOVIEROLE mr
ON a.A_ID = mr.A_ID AND
mr.MR_ROLE LIKE '%UNCREDITED%'
GROUP BY a.A_NAME;
Instead of having the conding in the where clause, you could move it to a case expression inside the count function, and utilize the fact that count skips nulls:
SELECT A_NAME, COUNT(CASE WHEN MR_ROLE LIKE '%UNCREDITED%' THEN 1 END)
FROM ACTOR
JOIN MOVIEROLE ON ACTOR.A_ID = MOVIEROLE.A_ID
GROUP BY A_NAME;

How to successfully use JOIN queries?

These are my schemas:
CREATE TABLE CUSTOMER
(
customerID numeric,
name text,
email varchar(320),
cell varchar,
address varchar,
flag text NULL,
PRIMARY KEY(customerID)
);
CREATE TABLE REFERRALS
(
customerID numeric NOT NULL,
name text NOT NULL,
PRIMARY KEY(customerID, name)
);
CREATE TABLE RENTAL
(
customerID numeric NOT NULL,
model numeric NOT NULL,
borrowDate timestamp NOT NULL,
dueDate date NOT NULL,
charge money NOT NULL,
returnDate timestamp NULL,
addFees money NULL,
notes text NULL,
PRIMARY KEY(customerID, model, borrowDate)
);
CREATE TABLE SCOOTER
(
model bigserial NOT NULL,
manufacturer text NOT NULL,
country text NOT NULL,
range numeric NOT NULL,
weight numeric NOT NULL,
topspeed numeric NOT NULL,
condition text NOT NULL,
availability text NOT NULL,
PRIMARY KEY(model)
);
For the first query, I want to show the model and manufacturer columns from SCOOTER, the name column from CUSTOMER, and the dueDate column from RENTAL, but only for in rows where SCOOTER.model = RENTAL.model and where RENTAL.returnDate is NULL. And finally, in descending order by dueDate.
This is the query I wrote:
SELECT
s.model, s.manufacturer, c.name, r.duedate
FROM
SCOOTER AS s, CUSTOMER AS c
INNER JOIN
RENTAL AS r ON r.model = s.model AND r.returnDate IS NULL
ORDER BY
r.duedate DESC;
I get this error however:
HINT: There is an entry for table "s", but it cannot be referenced from this part of the query.
STATEMENT: SELECT s.model, s.manufacturer, c.name, r.duedate FROM SCOOTER AS s, CUSTOMER AS c
INNER JOIN RENTAL AS r ON r.model = s.model AND r.returnDate IS NULL ORDER BY r.duedate desc;
ERROR: invalid reference to FROM-clause entry for table "s"
LINE 2: INNER JOIN RENTAL AS r ON r.model = s.model AND r.returnDate...
^
HINT: There is an entry for table "s", but it cannot be referenced from this part of the query.
Well, I think you should study a little bit better SQL. You only connect table RENTAL and SCOOTER, but you left out the connection with CUSTOMER.
Your code should probably look more like
SELECT SCOOTER.model, SCOOTER.manufacturer, CUSTOMER.name, RENTAL.duedate
FROM SCOOTER
INNER JOIN RENTAL ON RENTAL.model = SCOOTER.model
INNER JOIN CUSTOMER ON RENTAL.customerID = CUSTOMER.customerID
WHERE RENTAL.returnDate IS NULL ORDER BY RENTAL.duedate desc;
Hope it helps!
Cheers
You're mixing join styles there, something to be avoided. Joins look like this:
SELECT * FROM
a
INNER JOIN b ON a.column = b.column
INNER JOIN c ON a.column = c.column ...
Every row from a is connected to every row from b, where the ON clause is true. Then every row from a-b is connected to C again where the ON clause is true. This causes the data to grow sideways as more data from more tables is joined on. Tables can even be joined to themselves.
It's hard (and off topic for SO) to go into depth about every aspect of JOINs so some background reading will probably be essential

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

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.

SQL Anomaly Using 'USING' Clause with Nested Queries?

I have a normalized database containing 3 tables whose DDL is this:
CREATE CACHED TABLE Clients (
cli_id INTEGER GENERATED ALWAYS AS IDENTITY (START WITH 100) PRIMARY KEY,
defmrn_id BIGINT,
lastName VARCHAR(48) DEFAULT '' NOT NULL,
midName VARCHAR(24) DEFAULT '' NOT NULL,
firstName VARCHAR(24) DEFAULT '' NOT NULL,
doB INTEGER DEFAULT 0 NOT NULL,
gender VARCHAR(1) NOT NULL);
CREATE TABLE Client_MRNs (
mrn_id BIGINT GENERATED ALWAYS AS IDENTITY (START WITH 100) PRIMARY KEY,
cli_id INTEGER REFERENCES Clients ( cli_id ),
inst_id INTEGER REFERENCES Institutions ( inst_id ),
mrn VARCHAR(32) DEFAULT '' NOT NULL,
CONSTRAINT climrn01 UNIQUE (mrn, inst_id));
CREATE TABLE Institutions (
inst_id INTEGER GENERATED ALWAYS AS IDENTITY (START WITH 100) PRIMARY KEY,
loc_id INTEGER REFERENCES Locales (loc_id ),
itag VARCHAR(6) UNIQUE NOT NULL,
iname VARCHAR(80) DEFAULT '' NOT NULL);
The first table contains a foreign key column, defmrn_id, that is a reference to a "default identifier code" that is stored in the second table (which is a list of all identifier codes). A record in the first table may have many identifiers, but only one default identifier. So yeah, I have created a circular reference.
The third table is just normalized data from the second table.
I wanted a query that would find a CLIENT record based on matching a supplied identifier code to any of the identifier codes in CLIENT_MRNs that may belong to that CLIENT record.
My strategy was to first identify those records that matched in the second table (CLIENT_MRN) and then use that intermediate result to join to records in the CLIENT table that matched other user-supplied searching criteria. I also need to denormalize the identifier reference defmrn_id in the 1st table. Here is what I came up with...
SQL = SELECT c.*, r.mrn, i.inst_id, i.itag, i.iname
FROM Clients AS c
INNER JOIN
(
SELECT m.cli_id
FROM Client_MRNs AS m
WHERE m.mrn = ?
) AS m2 ON m2.cli_id = c.cli_id
INNER JOIN Client_MRNs AS r ON c.defmrn_id = r.mrn_id
INNER JOIN Institutions AS i USING ( inst_id )
WHERE (<other user supplied search criteria...>);
The above works, but I spent some time trying to understand why the following was NOT working...
SQL = SELECT c.*, r.mrn, i.inst_id, i.itag, i.iname
FROM Clients AS c
INNER JOIN
(
SELECT m.cli_id
FROM Client_MRNs AS m
WHERE m.mrn = ?
) AS m2 USING ( cli_id )
INNER JOIN Client_MRNs AS r ON c.defmrn_id = r.mrn_id
INNER JOIN Institutions AS i USING ( inst_id )
WHERE (<other user supplied search criteria...>);
It seems to me that the second SQL should work, but it fails on the USING clause every time. I am executing these queries against a database managed by HSQLDB 2.2.9 as the RDBMS. Is this a parsing issue in HSQLDB or is this a known limitation of the USING clause with nested queries?
You can always try with HSQLDB 2.3.0 (a release candidate).
The way you report the incomplet SQL does not allow proper checking. But there is an ovbious mistake in the query. If you have:
SELECT INST_ID FROM CLIENTS_MRS AS R INNER JOIN INSTITUTIONS AS I USING (INST_ID)
INST_ID can be used in the SELECT column list only without a table qualifier. The reason is it is no longer considered a column of either table. The same is true with common columns if you use NATURAL JOIN.
This query is accepted by version 2.3.0
SELECT c.*, r.mrn, inst_id, i.itag, i.iname
FROM Clients AS c
INNER JOIN
(
SELECT m.cli_id
FROM Client_MRNs AS m
WHERE m.mrn = 2
) AS m2 USING ( cli_id )
INNER JOIN Client_MRNs AS r ON c.defmrn_id = r.mrn_id
INNER JOIN Institutions AS i USING ( inst_id )