MySQL max and count aggregate functions - mysql

So I have three tables
CREATE TABLE Personnel(
IdPersonnel INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(45) NOT NULL,
Surename VARCHAR(45) NOT NULL,
Department VARCHAR(45) NOT NULL,
Salary INT NOT NULL,
Birthday DATE NOT NULL
);
CREATE TABLE Doctor(
IdDoctor INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
fk_Personnel INT NOT NULL
);
CREATE TABLE Visit(
IdVisit INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Date DATE NOT NULL,
ControlDate DATE,
fk_Patient INT NOT NULL,
fk_Doctor INT NOT NULL,
);
From these three tables, I need to get a doctor who had most of the visits.
SELECT p.Name, p.Surname, COUNT(*) visits
FROM Visit v
JOIN Doctor d
ON v.fk_Doctor = d.IdDoctor
JOIN Personnel p
ON d.fk_Personnel = p.IdPersonnel
GROUP BY d.IdDoctor
ORDER BY COUNT(*) DESC
LIMIT 1;
I used this query, and the result is correct, but I have to use MAX() function. I am using MySQL Community server 8.0.26

select P.Name, P.Surename, P.IdPersonnel ,count(*) from Visit V
inner join Doctor D on V.fk_Doctor = D.IdDoctor
inner join Personnel P on D.fk_Personnel = P.IdPersonnel
group by P.Name, P.Surename, P.IdPersonnel
having count(*) = (
select max(visits) from
(select fk_Doctor, count(*) visits from Visit group by fk_Doctor) a
)

Related

How to display multiple items but only the highest values in year and month for each?

I have the following two tables:
CREATE DATABASE IF NOT EXISTS springbootdb;
DROP TABLE IF EXISTS occupancy;
DROP TABLE IF EXISTS hotel;
CREATE TABLE hotel
(
id INT NOT NULL PRIMARY KEY auto_increment,
category int NOT NULL,
name TEXT NOT NULL,
owner TEXT NOT NULL,
contact TEXT NOT NULL,
address TEXT NOT NULL,
city TEXT NOT NULL,
zip TEXT NOT NULL,
phone TEXT NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE occupancy
(
id int not null primary key auto_increment,
hotelid int not null,
month int not null,
year int not null,
room_utilization int not null,
bed_utilization int not null,
room_count int not null,
bed_count int not null,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Now I want to display every single hotel.id and hotel.name along with occupancy.room_count, occupancy.bed_count, occupancy.room_utilization and occupancy.bed_utilization - but only the very latest entry for each hotel.id, so the ones where occupancy.year and occupancy.month are the highest values each.
I tried a couple of things, such as
SELECT springbootdb.hotel.id, springbootdb.hotel.name, springbootdb.occupancy.bed_count, springbootdb.occupancy.bed_utilization
From springbootdb.hotel
INNER JOIN springbootdb.occupancy
ON hotel.id = occupancy.hotelid
order by springbootdb.occupancy.`year`, springbootdb.occupancy.`month` asc limit 1;
but haven't had any success unfortunately.
Can a good soul tell me how to get there?
Thanks!
This is best solved with window functions, but that requires you upgrade to MySQL 8.0.
Here's the general idea.
SELECT t.id, t.name, t.bed_count, t.bed_utilization
FROM (
SELECT h.id, h.name, o.bed_count, o.bed_utilization, ROW_NUMBER() OVER (PARTITION BY h.id ORDER BY o.`year` DESC, o.`month` DESC) AS rownum
FROM hotel AS h JOIN occupacy AS o ON h.id = o.hotelid
) AS t
WHERE t.rownum = 1;

Using rows from the first query in the sub-query?

I have a bit complicated query:
SELECT SQL_CALC_FOUND_ROWS DISTINCT l1.item_id, l1.uid, l2.id, l2.uid, u.prename, l1.item_id, l2.item_id,
(SELECT SUM(cnt) FROM
(
SELECT DISTINCT
p1.item_id,
COUNT(*) AS cnt
FROM pages_likes AS p1
JOIN pages_likes AS p2 ON p1.item_id = p2.item_id AND p1.status = p2.status
WHERE p1.uid = 391 AND p2.uid = 1091
GROUP BY p1.id
ORDER BY p1.date DESC
) AS t) AS total
FROM pages_likes l1
JOIN users u on u.id = l1.uid
JOIN pages_likes l2 on l1.item_id = l2.item_id
JOIN users_likes ul on l1.uid = ul.uid
WHERE ul.date >= DATE_SUB(NOW(),INTERVAL 1 WEEK)
AND l1.uid != 1091 AND l2.uid = 1091
AND (l1.status = 1 AND l2.status = 1)
AND u.gender = 2
GROUP BY l1.uid
ORDER BY
total DESC,
l1.uid DESC,
l1.date DESC
What I expect: It should display all users, sorted by total page likes we have in common that also are the most liked users this week.
The thing is that I inserted values (391 and 1091) as user id to test the query. But since it should be dynamic I'll need to use the row of the first query l1.uid in the subquery, so it should be WHERE p1.uid = l1.uid AND p2.uid = 1091 but mysql can't find the row.
status = 1 means user liked this page, status = 0 means user disliked this page.
Table structure here:
CREATE TABLE pages_likes
(
id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
uid INT NOT NULL,
date DATETIME NOT NULL,
item_id INT,
status TINYINT
);
CREATE INDEX item_index ON pages_likes (item_id);
CREATE INDEX uid_index ON pages_likes (uid);
CREATE TABLE users
(
id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
fb_uid VARCHAR(255),
email VARCHAR(100) NOT NULL,
pass VARCHAR(50) NOT NULL,
gender TINYINT NOT NULL,
birthdate DATE,
signup DATETIME NOT NULL,
lang VARCHAR(10) NOT NULL,
username VARCHAR(255),
prename VARCHAR(255) NOT NULL,
surname VARCHAR(255) NOT NULL,
projects VARCHAR(255) NOT NULL,
views INT DEFAULT 0,
verified DATETIME
);
CREATE UNIQUE INDEX id_index ON users (id);
CREATE INDEX uid_index ON users (id);
CREATE TABLE users_likes
(
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
uid INT NOT NULL,
date DATETIME NOT NULL,
item_id INT,
status TINYINT
);
CREATE INDEX item_index ON users_likes (item_id);
CREATE INDEX uid_index ON users_likes (uid);
Have you tried different alias names for your subquery and then use alias from outer query? It works for me in this simple example: http://rextester.com/MJOL87502
Sadly I cannot test in your sqlfiddle, since that site often doesn't respond or throws errors (like it does now).
You could also use Window Functions and replace your subselect with something as simple as SUM(*) OVER (PARTITION BY p1.id, p1.item_id), but MySQL does not support Window Functions.

MYSQL SELECT query: How to concatenate 4 tables with AND and OR conditions?

I have four MySQL tables:
usuarios (have the list of all users and their personal information)
roles (have the list of available roles in the site, that determines their permissions)
rolesUsuarios (links each user with their roles. They can have more than one)
cursos (have the list of courses of the site with each course description)
cursosUsuarios (links each user with all the courses where they are enrolled)
I want to make a SELECT query that shows me for a specific user, all their personal information, the roles he has and the courses where he's enrolled.
I've created this query that does work:
SELECT usuarios.userID, usuarios.userEmail, usuarios.userApellido, usuarios.userNombres,
usuarios.userDNI, usuarios.userFechaGeneracion,
rolesUsuarios.userID, rolesUsuarios.nombreRol,
cursosUsuarios.userID, cursosUsuarios.cursoID,
cursos.cursoID, cursos.nombreCurso, cursos.cursoYear, cursos.cursoMes,
cursos.modalidadCurso,
GROUP_CONCAT(cursos.nombreCurso, ':', cursosUsuarios.cursoID ORDER BY cursosUsuarios.cursoID SEPARATOR ',') AS 'cursos'
FROM usuarios JOIN rolesUsuarios JOIN cursosUsuarios JOIN cursos
WHERE usuarios.userID='10'
AND rolesUsuarios.userID = usuarios.userID
AND cursosUsuarios.userID = usuarios.userID
AND cursosUsuarios.cursoID = cursos.cursoID
GROUP BY usuarios.userID
The problem is that if the user isn't enrolled in any course, it won't show any result. How may I do that?
These are each tables structure:
CREATE TABLE roles(
roleID int unsigned not null auto_increment primary key,
nombreRol char(50) not null
);
CREATE TABLE usuarios(
userID int unsigned not null auto_increment primary key,
userEmail char(50) null,
userApellido char(50) null,
userNombres char(50) null,
userDNI int(10) null,
userPass char(65) null,
userFechaGeneracion char(10) null
);
CREATE TABLE rolesUsuarios (
rolesUsuariosID int unsigned not null auto_increment primary key,
userID int not null,
nombreRol char(50) not null
);
CREATE TABLE cursos (
cursoID int unsigned not null auto_increment primary key,
nombreCurso char(100) not null,
cursoYear int(4) not null,
cursoMes char(3) not null,
cursoFechaInicio char(10) null,
modalidadCurso char(50) not null
);
CREATE TABLE cursosUsuarios (
cursosUsuariosID int unsigned not null auto_increment primary key,
userID int not null,
cursoID int not null
);
I think something like this will work:
SELECT usuarios.userID, usuarios.userEmail, usuarios.userApellido, usuarios.userNombres,
usuarios.userDNI, usuarios.userFechaGeneracion,
rolesUsuarios.userID, rolesUsuarios.nombreRol,
cursosUsuarios.userID, cursosUsuarios.cursoID,
cursos.cursoID, cursos.nombreCurso, cursos.cursoYear, cursos.cursoMes,
cursos.modalidadCurso,
GROUP_CONCAT(cursos.nombreCurso, ':', cursosUsuarios.cursoID ORDER BY cursosUsuarios.cursoID SEPARATOR ',') AS 'cursos'
FROM usuarios LEFT JOIN rolesUsuarios
ON usuarios.userID = rolesUsuarios.userID
LEFT JOIN cursosUsuarios
ON usuarios.userID = cursosUsuarios.userID
LEFT JOIN cursos
ON cursosUsuarios.cursoID = cursos.cursoID
WHERE usuarios.userID='10'
GROUP BY usuarios.userID;
Also, if you are selecting a single userID in the WHERE clause there is no need for the GROUP BY userID line.

Counting number of files in a folder in mysql

I have these tables
Create table series (
id_series Int NOT NULL AUTO_INCREMENT,
name_series Varchar(100) NOT NULL,
kcal Int NOT NULL DEFAULT 0,
type Varchar(20) NOT NULL,
id_user Int NOT NULL,
UNIQUE (name_excercise),
Primary Key (id_series)) ENGINE = MyISAM;
Create table excercise (
id_excercise Int NOT NULL AUTO_INCREMENT,
name_excercise Char(100) NOT NULL,
date Date NOT NULL,
start Time NOT NULL,
end Time NOT NULL,
km Double(10,2) NOT NULL,
id_series Int NOT NULL,
UNIQUE (id_excercise),
Primary Key (id_excercise)) ENGINE = MyISAM;
They are basically like folders. Series has a name of a series and inside, there are subsequent excercises. What I need is to return every series user has and the number of the excercises inside. But I am having trouble with it, since I am not passing any ID of a concrete series in which it could count all the excercises. Is it possible to print that with only a user id?
I basically have this:
"SELECT * FROM series WHERE id_user = $id_user"
but need to combine it with this:
"SELECT COUNT(*) FROM excercise WHERE id_series = $id_series"
without the need of id_series.
This is the query:
SELECT S.*, COUNT(E.id_excercise) AS NumOfExc
FROM series AS S
JOIN excercise AS E
ON S.id_series = E.id_series
WHERE S.id_user = '$id_user'
GROUP BY E.id_series
UNION
SELECT *, 0 AS NumOfExc
FROM series
WHERE id_user = '$id_user'
AND id_series NOT IN (
SELECT S.id_series AS id_series
FROM series AS S
JOIN excercise AS E
ON S.id_series = E.id_series
WHERE S.id_user = '$id_user'
GROUP BY E.id_series
)
The above query will show what you need.

select the max value and other values from three different tables

I have three tables with the following structure:-
CREATE TABLE `contract` (
`conid` int(11) NOT NULL AUTO_INCREMENT,
`servName` int(11) NOT NULL,
`cid` int(11) NOT NULL,
`term` int(11) DEFAULT NULL,
`monthly_charge` double NOT NULL,
`start_date` date NOT NULL,
`expiry_Date` date NOT NULL,
`next_PayDate` date DEFAULT NULL,
`status` tinyint(4) NOT NULL,
PRIMARY KEY (`conid`),
UNIQUE KEY `servName` (`servName`,`cid`)
)
CREATE TABLE `servicetype` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`serviceName` varchar(255) NOT NULL,
PRIMARY KEY (`sid`)
)
CREATE TABLE `transactions` (
`tid` int(11) NOT NULL AUTO_INCREMENT,
`conid` int(11) NOT NULL,
`amount` double NOT NULL,
`paidate` date NOT NULL,
`descr` text NOT NULL,
PRIMARY KEY (`tid`)
)
What I want to get is the latest transaction for a particular user i.e:-
conid, serviceName, cid, term, monthly_charge, start_date, expiry_Date, next_PayDate, amount, paidate, descr
And this is the select statement I am using the get to that information:-
SELECT c.conid, serviceName, cid, term, monthly_charge, start_date, expiry_Date, next_PayDate, status, amount, paidate, descr
FROM servicetype s
LEFT JOIN contract c on s.sid = c.servName
LEFT JOIN transactions t ON c.conid=t.conid
WHERE cid = 4 AND status = 1
The statement works but, it is giving me all transactions belonging to cid 4 and all I want to display is only the latest transaction belonging to the said contract it (conid).
Thanking you in advance for your time and effort.
Sounds like you want to use MySQL's GROUP BY to group all of the results by a specific cid, and then use a HAVING condition to get the MAX() transaction:
SELECT
c.conid, serviceName, cid, term, monthly_charge, start_date, expiry_Date, next_PayDate, status, amount, paidate, descr
FROM servicetype s
LEFT JOIN contract c on s.sid = c.servName
LEFT JOIN transactions t ON c.conid=t.conid
WHERE
cid = 4 AND status = 1
GROUP BY cid
HAVING t.paidate = MAX(t.paidate)
You can JOIN twice on the transactions table. The first join gets the max() date for each conid and then the second join will return the details of that max transaction:
select c.conid,
s.serviceName,
c.cid,
c.term,
c.monthly_charge,
c.start_date,
c.expiry_date,
c.next_PayDate,
c.status,
t2.amount,
t1.paidate,
t2.descr
FROM servicetype s
LEFT JOIN contract c
on s.sid = c.servName
LEFT JOIN
(
SELECT max(paidate) paidate, conid
FROM transactions
GROUP BY conid
) t1
ON c.conid=t1.conid
LEFT JOIN transactions t2
ON t1.paidate = t2.paidate
AND t1.conid = t2.conid
WHERE c.cid = 4
AND c.status = 1
Edit, based on your comments the query should be:
select c.conid,
s.serviceName,
c.cid,
c.term,
c.monthly_charge,
c.start_date,
c.expiry_date,
c.next_PayDate,
c.status,
t3.amount,
t3.paidate,
t3.descr
FROM servicetype s
LEFT JOIN contract c
on s.sid = c.servName
LEFT JOIN
(
SELECT max(paidate) paidate, conid, max(tid) tid
FROM transactions t
GROUP BY conid
) t1
on c.conid = t1.conid
LEFT JOIN transactions t3
on t1.conid = t3.conid
and t1.paidate = t3.paidate
and t1.tid = t3.tid
where c.cid = 4
and c.status = 1
GROUP BY c.conid;
See SQL Fiddle With Demo