SQLITE3 Sub-querys - sql

I'm having a problem, that i can't figure it out, even after reserching here and at sqlite.org
So, I have these tables:
CREATE TABLE MEDICO(
idMedico INTEGER PRIMARY KEY AUTOINCREMENT,
nome VARCHAR(50) NOT NULL,
morada VARCHAR(50) NOT NULL,
telefone VARCHAR(9) NOT NULL
);
CREATE TABLE PRESCRICAO(
idPrescricao INTEGER PRIMARY KEY AUTOINCREMENT,
idConsulta INTEGER,
idMedico INTEGER NOT NULL,
nrOperacional INTEGER NOT NULL,
FOREIGN KEY(idConsulta) REFERENCES CONSULTA(idConsulta),
FOREIGN KEY(idMedico) REFERENCES MEDICO(idMedico),
FOREIGN KEY(nrOperacional) REFERENCES UTENTE(nrOperacional)
);
CREATE TABLE PRESCRICAO_MEDICAMENTO(
idPrescricao INTEGER ,
idMedicamento INTEGER,
nrEmbalagens INTEGER NOT NULL,
FOREIGN KEY(idPrescricao) REFERENCES PRESCRICAO(idPrescricao),
FOREIGN KEY(idMedicamento) REFERENCES MEDICAMENTO(idMedicamento),
PRIMARY key(idPrescricao, idMedicamento)
);
I want the idMedicamento that is the most used by the MEDICO lets say with idMedico=7,
until here, everything's fine, i'm doing:
SELECT idmedicamento, MAX(total) as maximum
FROM (SELECT idMedicamento, COUNT(idMedicamento) AS total
FROM PRESCRICAO_MEDICAMENTO
WHERE PRESCRICAO_MEDICAMENTO.idPrescricao IN (
SELECT idPrescricao FROM PRESCRICAO
WHERE PRESCRICAO.idmedico= 7
)
GROUP BY idMedicamento);
and i get:
IDmedicamento:3
maximum:5
wich is something that I want and it is correct.
but when i do:
SELECT idMedicamento
FROM (SELECT idMedicamento, MAX(total) as maximum
FROM (SELECT idMedicamento, COUNT(idMedicamento) AS total
FROM PRESCRICAO_MEDICAMENTO
WHERE PRESCRICAO_MEDICAMENTO.idPrescricao IN (
SELECT idPrescricao FROM PRESCRICAO
WHERE PRESCRICAO.idmedico= 7
)
GROUP BY idMedicamento));
All i get is the last used idMedicamento by the MEDICO, in this case, MEDICAMENTO with idMedicamento=5.
Any idea what i'm doing wrong? Really can't figure it out.
Thanks

In many cases, the easiest way to get other columns from the record with a maximum value is to use ORDER BY/LIMIT 1:
SELECT idMedicamento
FROM PRESCRICAO_MEDICAMENTO
WHERE idPrescricao IN (SELECT idPrescricao
FROM PRESCRICAO
WHERE idmedico = 7)
GROUP BY idMedicamento
ORDER BY COUNT(*) DESC
LIMIT 1

The second query is wrong because the first query is wrong; I don't think it's valid SQL, and I'm not sure why it's working for you.
The inner query (which I'll call Q1) of the first query is ok: SELECT id, COUNT(id) AS total FROM ... GROUP BY id;
But the outer query of the first query is broken: SELECT id, MAX(total) FROM ...; without a GROUP BY. This is wrong because the MAX forces an aggregation over the entire table (which is what you want), but the 'id' is not aggregated.
If you remove 'id, ' from the query, you should correctly get the maximum: SELECT MAX(total) AS maximum FROM ...; which I'll call Q2.
Then it gets ugly, because SQLite doesn't support CTEs. Basically it is:
SELECT id FROM (Q1) WHERE total = (Q2);
but you have to write out Q1 and Q2, and there's a lot of repetition because Q2 includes Q1.

Related

How to select from table A and then insert selected id inside table B with one query?

I'm trying to implement a very basic banking system.
the goal is to have different types of transactions ( deposit, withdraw, transfer ) inside a table and refer to them as IDs inside transaction tables.
CREATE TABLE transaction_types (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
name VARCHAR UNIQUE NOT NULL
)
CREATE TABLE transactions (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
type_id INTEGER NOT NULL,
amount FLOAT NOT NULL
)
What I'm trying to accomplish is:
When inserting into transactions table no record can have an invalid type_id ( type_id should exist in transaction_types table )
First of all get type_id from transaction_types table and then insert inside transactions table, with one query ( if it's possible, I'm fairly new )
I'm using Node.js/Typescript and PostgreSQL, any help is appreciated a lot.
For (1): modify Transactions table definition by adding REFERENCES transaction_types(id) to the end of the type_id column definition prior to the comma.
For (2), assuming you know the name of the transaction_type, you can accomplish this by:
INSERT INTO transactions(type_id, amount)
VALUES ((SELECT id from transaction_types WHERE name = 'Withdrawal'), 999.99)
By the way, my PostgreSQL requires SERIAL instead of INTEGER AUTOINCREMENT

Oracle SQL Check

I'm trying to implement an Oracle SQL database, in one of my tables I must introduce a restriction which does not allow to have more than 4 people in the same group:
I've tried this:
CREATE TABLE PERSON (name VARCHAR (20) PRIMARY KEY, group VARCHAR (3), CHECK (COUNT (*) group FROM PERSON) <=4);
also this (among others):
CREATE TABLE PERSON (name VARCHAR (20) PRIMARY KEY, group VARCHAR (3), CHECK NOT EXISTS (Select COUNT(*) FROM PERSON GROUP BY group HAVING COUNT(*) > 4);
But I'm getting errors every time (ORA-00934: group function is not allowed here or ORA-02251: subquery not allowed here.
What is the correct way to do it?
You have multiple issues with this
CREATE TABLE PERSON (
name VARCHAR(20) PRIMARY KEY,
group VARCHAR(3),
CHECK (COUNT (*) group FROM PERSON) <=4);
);
Oracle explicitly prefers VARCHAR2() to VARCHAR().
GROUP is a really bad name for a column, because it is a keyword. Surely you can find something like group_name or whatever for the name.
CHECK constraints only work within a single row.
Probably the best way to handle this is:
Create a new table called groups -- or whatever. It should have a group_id as well as group_name and num_persons.
Add triggers to person to keep the counter up-to-date for inserts, deletes, and updates to person.
Add a check constraint to groups, say check (num_persons <= 4).
You need to create the table as following:
CREATE TABLE PERSON (
name VARCHAR2(20) PRIMARY KEY,
group_ VARCHAR2(3) -- added _ after column name
); -- used varchar2 as data type of column
Then create before insert trigger as following:
create trigger person_trg
before insert on person
for each row
declare
group_cnt number;
begin
select count(distinct name)
into group_cnt
from person
where group_ = :new.group_;
if group_cnt = 4 then
raise_application_error(-20001, 'more than 4 persons are not allowed in the group');
end if;
end;
/
I have used distinct person name as more than 4 distinct persons are not allowed in the group as per your requirement.
db<>fiddle demo
Cheers!!

Distinct top 10 from multiple tables

I have these two tables in SQLite
CREATE TABLE "freq" (
`id` INTEGER,
`translation_id` INTEGER,
`freq` INTEGER DEFAULT NULL,
`year` INTEGER,
PRIMARY KEY(`id`),
FOREIGN KEY(`translation_id`) REFERENCES `translation`(`id`) DEFERRABLE INITIALLY DEFERRED
)
CREATE TABLE translation (
id INTEGER PRIMARY KEY,
w_id INTEGER,
word TEXT DEFAULT NULL,
located_in TEXT,
UNIQUE (word, language)
ON CONFLICT ABORT
)
Based on the values from these tables I want to create a third one which contains the top 10 words for every translation.located_in for every freq.year. This could look like this:
CREATE TABLE top_ten_words_by_country (
id INTEGER PRIMARY KEY,
located_in TEXT,
year INTEGER,
`translation_id` INTEGER,
freq INTEGER,
FOREIGN KEY(`translation_id`) REFERENCES `translation`(`id`) DEFERRABLE INITIALLY DEFERRED
)
Thats what I tried (for one country and one year) so far:
SELECT * FROM freq f, translation t
WHERE t.located_in = 'Germany' ORDER BY f.freq DESC
which has these problems:
it doesn't add up multiple words from translation which have the same w_id (which means they are a translation from each other)
it only works for one year and one country
it takes veeeeery long (I know joins are expensive, so its not that important to speed this up)
it contains duplicate translation.word
So can anyone provide me a way to do what I want?
The speed is the least important thing here for me.
Look, you have a cartesian product(there's no relation between your tables).
Besides, you have to use 'group by' clause.
And you can create a view instead a table.
Change your query to:
SELECT sum(f.freq) total_freq
, t.w_id
, t.located_in
, f.year
FROM freq f
, translation t
WHERE f.translation_id = t.id
group by t.w_id
, t.located_in
, f.year
ORDER BY total_freq DESC

How can I get this query to print a record that only exists in one table?

I am trying to create a query that will accept a date from the user and display information from two tables based on this date. This works for all of my tests except my last test. My last test, I enter a date that should return a record that only exists in the expmast table and does not exist in the expbycc table. When I enter the date to try and get this record to be returned, it tells me no records have been found. I know this is because in my where, i have an AND that checks if M.ExpNum = C.ExpNUm which isn;t true for this record because it only exists in one table. I can not figure out how to get this query to work. Any help/advice is greatly appreciated. Below is my script, followed by the table structures used for this query, thank you.
Script:
ACCEPT Date PROMPT 'Enter a date:';
SELECT M.ExpNum, EDate, IsCash, StoreCode, CashAmt, CType, CCNum, Amt
FROM ExpMast M, ExpByCc C
WHERE EDate = to_date('&Date','mm-dd-yy')
AND M.ExpNum = C.ExpNum;
Tables:
CREATE TABLE EXPMAST
(ExpNum NUMBER(2,0) NOT NULL PRIMARY KEY,
EDate DATE,
IsCash VARCHAR2(1),
StoreCode VARCHAR2(4),
CONSTRAINT fk_STORE_EXPMAST FOREIGN KEY (StoreCode)
REFERENCES STORE (Code)
);
CREATE TABLE ExpByCC
(ExpNum NUMBER(2,0) NOT NULL,
CType VARCHAR2(1) NOT NULL,
CCNum VARCHAR2(16) NOT NULL,
Amt DECIMAL(5,2),
CONSTRAINT fk_CRCARD_ExpByCC FOREIGN KEY (CType, CCNum)
REFERENCES CRCARD (CType, CCNum),
CONSTRAINT fk_EXPMAST_ExpByCC FOREIGN KEY (ExpNum)
REFERENCES EXPMAST (ExpNum),
CONSTRAINT pk_ExpByCC PRIMARY KEY (ExpNum, CType, CCNum)
);
You need a left outer join. And you can't express an outer join using your implicit join syntax. You want to use explicit joins in the from clause.
A simple rule: NEVER use commas in the from clause.
Now, it is easy:
SELECT M.ExpNum, EDate, IsCash, StoreCode, CashAmt, CType, CCNum, Amt
FROM ExpMast M LEFT OUTER JOIN
ExpByCc C
ON M.ExpNum = C.ExpNum AND
WHERE M.EDate = to_date('&Date','mm-dd-yy') AND
C.ExpNum IS NULL;

Why does this query only select a single row?

SELECT * FROM tbl_houses
WHERE
(SELECT HousesList
FROM tbl_lists
WHERE tbl_lists.ID = '123') LIKE CONCAT('% ', tbl_houses.ID, '#')
It only selects the row from tbl_houses of the last occuring tbl_houses.ID inside tbl_lists.HousesList
I need it to select all the rows where any ID from tbl_houses exists within tbl_lists.HousesList
It's hard to tell without knowing exactly what your data looks like, but if it only matches the last ID, it's probably because you don't have any % at the end of the string, so as to allow for the list to continue after the match.
Is that a database in zeroth normal form I smell?
If you have attributes containing lists of values, like that HousesList attribute, you should instead be storing those as distinct values in a separate relation.
CREATE TABLE house (
id VARCHAR NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE list (
id VARCHAR NOT NULL,
PRIMARY KEY (id),
);
CREATE TABLE listitem (
list_id VARCHAR NOT NULL,
FOREIGN KEY list_id REFERENCES list (id),
house_id VARCHAR NOT NULL,
FOREIGN KEY house_id REFERENCES house (id),
PRIMARY KEY (list_id, house_id)
);
Then your distinct house listing values each have their own tuple, and can be selected like any other.
SELECT house.*
FROM house
JOIN listitem
ON listitem.house_id = house.id
WHERE
listitem.list_id = '123'