mysql query hierarchic - mysql

I need help to find a sql query
CREATE TABLE album (
id_album INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(255) NOT NULL
);
CREATE TABLE sous_album (
id_sous_album INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(255) NOT NULL,
id_album INTEGER REFERENCES album(id_album)
);
CREATE TABLE photo (
id_photo INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(255) NOT NULL,
id_sous_album INTEGER REFERENCES sous_album(id_sous_album)
);
INSERT INTO album(id_album, nom) VALUES
('1', 'album1'),
('2', 'album2'),
('3', 'album3')
;
INSERT INTO sous_album(id_sous_album, nom, id_album) VALUES
('1', 'sous album 1', '1'),
('2', 'sous album 2', '1'),
('3', 'sous album 3', '1'),
('4', 'sous album 4', '2'),
('5', 'sous album 5', '2'),
('6', 'sous album 6', '3')
;
INSERT INTO photo(id_photo, nom, id_sous_album) VALUES
('1', 'photo1', '1'),
('2', 'photo2', '1'),
('3', 'photo3', '1'),
('4', 'photo4', '1'),
('5', 'photo5', '2'),
('6', 'photo6', '2'),
('7', 'photo7', '3'),
('8', 'photo8', '3'),
('9', 'photo9', '3'),
('10', 'photo10', '4'),
('11', 'photo11', '5'),
('12', 'photo12', '6'),
('13', 'photo13', '6'),
('14', 'photo14', '6'),
('15', 'photo15', '6')
;
SELECT distinct album.nom, count(photo.id_photo)
from album, photo, sous_album
where album.id_album=sous_album.id_album
AND sous_album.id_sous_album=photo.id_sous_album
group by album.nom
order by album.nom`
this query give me the name of the album and the number of photos but i want also the sous album and their numbers of photo... can u help me ?
the result :
nom | count(photo.id_photo)
album1 | 9
album2 | 2
album3 | 4
i need this result :
nom | count(photo.id_photo)
album1 | 9
sous album 1 | 4
sous album 2 | 2
sous album 3 | 3
album2 | 2
sous album 4 | 1
sous album 5 | 1
album3 | 4
sous album 6 | 4

SELECT x.*
, y.*
FROM
( SELECT a.nom
, COUNT(*) total
FROM album a
JOIN sous_album ap
ON ap.id_album = a.id_album
JOIN photo p
ON p.id_sous_album = ap.id_sous_album
GROUP
BY a.nom
) x
JOIN
( SELECT a.nom nom_album
, ap.nom nom_sous_ablum
, COUNT(*) total
FROM album a
JOIN sous_album ap
ON ap.id_album = a.id_album
JOIN photo p
ON p.id_sous_album = ap.id_sous_album
GROUP
BY a.nom
, ap.nom
) y
ON y.nom_album = x.nom;
Note that this particular query will only return an album if there is at least one sous_album (and photo) associated with it.

thanks for your solution... i find a other one...
SELECT a.nom, sa.nom, COUNT( * ) AS NbPhoto FROM album a INNER JOIN sous_album sa ON sa.id_album = a.id_album INNER JOIN photo p ON p.id_sous_album = sa.id_sous_album GROUP BY a.nom, sa.nom WITH ROLLUP

Related

Query for any person has any account of type x

Imagine I have two tables, Person and Account, a person can have accounts (type 1 and/or 2).
I'd like to get a list of people who have at least one type 1 account, and also get a list of people who don't have a type 1 account. I'm using Query #1 and #2 for this respectively but I think I'm doing something is wrong because the results do not match.
Schema (MySQL v5.7)
CREATE TABLE Person (
`PersonId` INTEGER,
`Name` VARCHAR(5)
);
INSERT INTO Person
(`PersonId`, `Name`)
VALUES
('1', 'Leo'),
('2', 'Natan'),
('3', 'Vera'),
('4', 'Julio'),
('5', 'Mary');
CREATE TABLE Accounts (
`AccountId` INTEGER,
`PersonId` INTEGER,
`Type` INTEGER
);
INSERT INTO Accounts
(`AccountId`, `PersonId`, `Type`)
VALUES
('1', '1', '0'),
('2', '1', '1'),
('3', '2', '0'),
('4', '2', '0'),
('5', '3', '1'),
('6', '4', '0'),
('7', '1', '0'),
('8', '2', '0');
Query #1
SELECT * FROM Person AS PD
LEFT JOIN Accounts AS AC ON AC.PersonId = PD.PersonId
WHERE AC.Type = 1;
PersonId
Name
AccountId
PersonId
Type
1
Leo
2
1
1
3
Vera
5
3
1
Query #2
SELECT * FROM Person AS PD
LEFT JOIN Accounts AS AC ON AC.PersonId = PD.PersonId
WHERE AC.Type = 0;
PersonId
Name
AccountId
PersonId
Type
1
Leo
1
1
0
1
Leo
7
1
0
2
Natan
3
2
0
2
Natan
4
2
0
2
Natan
8
2
0
4
Julio
6
4
0
View on DB Fiddle
EXISTS and NOT EXISTS are the more suitable solutions for this requirement:
-- Account type = 1
SELECT p.* FROM Person AS p
WHERE EXISTS (
SELECT *
FROM Accounts AS a
WHERE a.PersonId = p.PersonId AND a.Type = 1
);
-- No type 1 account
SELECT p.* FROM Person AS p
WHERE NOT EXISTS (
SELECT *
FROM Accounts AS a
WHERE a.PersonId = p.PersonId AND a.Type = 1
);
See the demo.

How to get rows ordered by desc from specific ids

My goal : Getting Purchase requests ordered by most confirmed shipment locations.
Purchase requests are linked to a shipment location (warehouse).
I have a table :
CREATE TABLE IF NOT EXISTS `shipment_locations` (
`id` int(6) primary key,
`name` varchar(200) NOT NULL
);
INSERT INTO `shipment_locations` (`id`, `name`) VALUES
('1', 'france'),
('2', 'usa'),
('3', 'spain'),
('4', 'germany');
CREATE TABLE IF NOT EXISTS `purchase_requests` (
`id` int(6) primary key,
`name` varchar(200) NOT NULL,
`total_cost_confirmed` int(6) NULL,
`shipment_location_id` int(6) NULL,
FOREIGN KEY (`shipment_location_id`) REFERENCES `shipment_locations` (`id`)
);
INSERT INTO `purchase_requests` (`id`, `name`, `total_cost_confirmed`, `shipment_location_id`) VALUES
('1', 'pr1', '109', 1),
('2', 'pr2', '1500', 3),
('3', 'pr3', '3000', 2),
('4', 'pr4', '10000', 2),
('5', 'pr5', '5', 3),
('6', 'pr6', '3000', 2),
('7', 'pr7', '3000', 2),
('8', 'pr8', '1', 3),
('9', 'pr9', '10000000', 3);
For ordering by shipment location that have the most confirmed cost, it's pretty simple :
SELECT shipment_location_id, SUM(total_cost_confirmed) totalConfirmed
FROM purchase_requests
GROUP BY shipment_location_id
ORDER BY totalConfirmed DESC
It works perfectly here : http://sqlfiddle.com/#!9/732f32/2/0
But, then I tried to filter by purchase request id (adding GROUP BY id and WHERE id IN(...)) it gives me the wrong order (because it's taking ids present in the result).
=> (sqlfiddle)
How I can keep the correct order from the first query while filtering by Purchase request id ?
Adding sqlfiddle : Sqlfiddle
Thanks by advance for your help :)
First aggregate to get the sum of totalConfirmed and then join to the table:
SELECT p.id, p.name, p.shipment_location_id, t.totalConfirmed
FROM purchase_requests p
INNER JOIN (
SELECT shipment_location_id, SUM(total_cost_confirmed) totalConfirmed
FROM purchase_requests
GROUP BY shipment_location_id
) t ON t.shipment_location_id = p.shipment_location_id
WHERE p.id IN ('1', '3', '4', '8')
ORDER BY t.totalConfirmed DESC
See the demo.
Results:
> id | name | shipment_location_id | totalConfirmed
> -: | :--- | -------------------: | -------------:
> 4 | pr4 | 2 | 19000
> 3 | pr3 | 2 | 19000
> 8 | pr8 | 1 | 110
> 1 | pr1 | 1 | 110

inner join with subquery results differs for these data sets

CREATE TABLE IF NOT EXISTS `wcd` (
`id` int(6) unsigned NOT NULL,
`wid` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `wcd` (`id`, `wid`) VALUES
('168', '5685'),
('167', '5685'),
('166', '5685'),
('165', '5685'),
('164', '5685'),
('163', '5685'),
('162', '5684'),
('161', '5684');
CREATE TABLE IF NOT EXISTS `cases` (
`id` int(6) unsigned NOT NULL,
`wcd_id` int(11) unsigned NOT NULL,
`reason_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `cases` (`id`, `wcd_id`, `reason_id`) VALUES
('20', '168', '4'),
('19', '168', '1'),
('18', '167', '6'),
('17', '167', '5'),
('16', '166', '4'),
('15', '166', '1'),
('14', '165', '4'),
('13', '165', '1'),
('12', '164', '1'),
('11', '163', '4'),
('10', '162', '1'),
('9', '162', '4'),
('8', '162', '5'),
('7', '161', '5'),
('6', '161', '6');
the above two table has foreignkey relation with wcd.id = cases.wcd_id,
Lets consider the records related to wcd.wid 5865. The result should be grouped by reason_id with the condition max(cases.id)
I used the query below to achieve this and got the result as expected.
SELECT d.id, d.wid, c.* FROM wcd d
LEFT JOIN cases c ON c.wcd_id = d.id
inner JOIN (SELECT MAX(id) AS max_id FROM cases GROUP BY reason_id) c2
ON c2.max_id = c.id
WHERE d.wid = 5685;
Result:
id wid id wcd_id reason_id
168 5685 19 168 1
168 5685 20 168 4
167 5685 17 167 5
167 5685 18 167 6
with the same query for 5684, the query returns 0 rows though there is data available for it. but I'm expecting the rows below.
id wid id wcd_id reason_id
162 5684 10 162 1
162 5684 9 162 4
162 5684 8 162 5
161 5684 6 161 6
What the issue with the query and what needs to be changed to get the result above for 5684.?
here is the sqlfiddle link
You need to look back at the wcd table to propery correlate, since you need the id of the row that has the "latest" reason per wid - and that column is not available in cases.
In MySQL 8.0, we would just use row_number()... but you tagged your question MySQL 5.6. I find that the simplest way to express this is with a correlated subquery:
SELECT d.id, d.wid, c.*
FROM wcd d
INNER JOIN cases c ON c.wcd_id = d.id
WHERE c.id = (
SELECT max(c2.id)
FROM wcd d2
INNER JOIN cases c2 ON c2.wcd_id = d2.id
WHERE d2.wid = d.wid AND c2.reason_id = c.reason_id
)
AND d.wid = 5685;
Then you must use MIN and get rid of the Where Clause.because
('162', '5684')
('161', '5684')
because
SELECT
d.id
, d.wid
,
c.*
FROM
wcd d
LEFT JOIN
cases c
ON c.wcd_id = d.id
inner JOIN (SELECT MIN(id) AS min_id FROM cases GROUP BY reason_id) c2
ON c2.min_id = c.id
see http://sqlfiddle.com/#!9/fb4569/26

Ties on Hall of Fame (group player, max level then max score for each game when month is...)

Need to list a Hall of Fame of best players, the database contains each single game player in different games.
The level has the priority, if the level are the same, check the highest score.
I've a database with user_id, level, score, game and data. Schema here:
CREATE TABLE IF NOT EXISTS `docs` (`user_id` int(6) unsigned NOT NULL,
`level` int(3) unsigned NOT NULL,`game` varchar(30) NOT NULL,
`score` int(5) unsigned NOT NULL,
`data` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `docs` (`user_id`, `level`, `game`, `score`,`data`) VALUES
('1', '7', 'pacman', '8452','2018-02-14 15:00:00'),
('1', '9', 'pacman', '9999','2018-02-10 16:30:00'),
('2', '8', 'pacman', '8500','2018-02-24 17:30:00'),
('1', '10', 'pacman', '9100','2018-02-15 18:30:00'),
('1', '10', 'pacman', '8800','2018-02-15 18:11:00'),
('1', '11', 'snake', '9600','2018-02-14 15:00:00'),
('1', '6', 'snake', '7020','2018-02-11 11:30:00'),
('2', '8', 'snake', '8500','2018-02-24 14:00:00'),
('2', '12', 'snake', '9200','2018-02-25 19:00:00'),
('2', '12', 'snake', '9800','2018-02-25 19:20:00'),
('1', '4', 'pacman', '2452','2018-03-11 15:00:00'),
('1', '6', 'pacman', '4999','2018-03-07 16:30:00'),
('2', '7', 'pacman', '5500','2018-03-02 17:30:00'),
('1', '7', 'pacman', '5100','2018-03-01 18:30:00'),
('1', '3', 'snake', '3600','2018-03-03 15:00:00'),
('1', '5', 'snake', '4220','2018-03-01 11:30:00'),
('2', '5', 'snake', '3900','2018-03-04 14:00:00'),
('2', '5', 'snake', '5200','2018-03-05 19:00:00');
i want retrieve the hall of fame for selected month and game,
for example if i choose pacman on march the result should be:
user level score
2 7 5500
1 7 5100
i tryed this how suggest in other similar topic
select d1.*
from docs d1
left outer join docs d2
on (d1.user_id = d2.user_id and d1.level < d2.level)
where d2.user_id is null
order by level desc;
but i've duplicate levels for same user, then i cant choose the game or the month.
here there is the SQL Fiddle
SELECT x.* FROM docs x
JOIN
(select user_id
, game
, MONTH(data) month
, MAX(score) score
from docs
where game = 'pacman'
and MONTH(data) = 3
group
by user_id
, game
, MONTH(data)
) y
ON y.user_id = x.user_id
AND y.game = x.game
AND y.month = MONTH(x.data)
AND y.score = x.score;
or something like that
after a long work, study and research this is the best solution for me:
SELECT user_id, level, score, game
FROM (
SELECT *,
#rn := IF(user_id = #g, #rn + 1, 1) rn,
#g := user_id
FROM (select #g := null, #rn := 0) x,
docs where game='pacman'
ORDER BY user_id, level desc, score desc, game
) X
WHERE rn = 1 order by level desc, score desc;
the explanation is in this topic Select one value from a group based on order from other columns

SQL Query to select records based on 2 different values in the same field and a condition

I Created a table like this for indian railways project:
CREATE TABLE IF NOT EXISTS `dennis` (
`trid` varchar(50) NOT NULL,
`place` varchar(50) NOT NULL,
`si` varchar(50) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
then i inserted rows this way :
INSERT INTO `dennis` (`trid`, `place`, `si`) VALUES
('100', 'cochi', '3'),
('300', 'cochi', '1'),
('100', 'mumbai', '1'),
('100', 'bangalore', '2'),
('300', 'bangalore', '2'),
('300', 'mumbai', '3'),
('200', 'hyderabad', '1'),
('400', 'trivandrum', '1'),
('200', 'bangalore', '2'),
('200', 'trivandrum', '3'),
('400', 'bangalore', '2'),
('400', 'hyderabad', '3');
My problem is when i select start station as Bangalore and destination as mumbai, I am getting all the train numbers because bangalore exist for all trid ie trainid but mumbai exist only for 100 and 300.
I need a query that can return only those trid who have both mumbai and bangalore. Also the si ie Serialnumber of bangalore must be lesser than si of mumbai.
i used this query but it seems to return all the record
SELECT DISTINCT trid FROM dennis WHERE place ='mumbai' OR place='bangalore'
try this,
SELECT DISTINCT d1.trid
FROM dennis d1
INNER JOIN dennis d2 ON d2.trid=d1.trid
WHERE d1.place = 'bangalore' and d2.place = 'mumbai' AND d1.si < d2.si
hope this answers your question
SELECT d1.trid
FROM dennis d1
INNER JOIN dennis d2 ON d2.trid=d1.trid
WHERE d1.place = 'bangalore' and d2.place = 'mumbai'