Order of SQL Query Clauses - mysql

I ran into a situation where I don't seem to get what SQL is doing. I have the following table and want to give out all the sorts of coffee which have the most amount of rating=5 with the amount itself.
create table likes
(
CName varchar(30),
UName varchar(30),
Rating int
);
insert into likes (CName, UName, Rating)
values ('Java', 'Klaus', '5'),
('Super', 'Klaus', '5'),
('MP', 'Klaus', '3'),
('Java', 'Marc', '5'),
('Mp', 'Marc', '5'),
('Super', 'Marc', '2'),
('Java', 'Nine', '2'),
('Super', 'Nine', '0'),
('MP', 'Karo', '3'),
('Super', 'Fabian', '4');
However this solution doesn't work as intended
SELECT
favcof.CName, favcof.cnt
FROM
(SELECT l.CName, COUNT(CName) cnt
FROM likes l
WHERE l.rating = 5
GROUP BY CName) favcof
WHERE
favcof.cnt = (SELECT MAX(favcof.cnt))
It executes as if there is no outer where-clause and gives out all sorts of coffees with their amount of rating = 5.

The expression (select max(favcof.cnt)) doesn't do anything. You can just drop the select and you will get favcof.cnt = favcof.cnt.
This is a little complicated, because favcof.cnt = max(favcof.cnt) would generate a syntax error because aggregation functions are not allowed in the where clause. So, the select subquery is actually an aggregation subquery with no from. Because there is only one value, it returns that value.
You want a correlated subquery. This would look like:
SELECT favcof.CName, favcof.cnt
FROM (SELECT l.UName, count(UName) as cnt
FROM likes l
WHERE l.rating=5
GROUP BY UName
) favcof
WHERE favcof.cnt = (SELECT MAX(favcof2.cnt)
FROM (SELECT l2.UName, count(l2.UName) as cnt
FROM likes l2
WHERE l2.rating=5
GROUP BY l2.UName
) favcof2
);
There are definitely other ways to write this query. However, this should help you understand why your version does not do what you want it to do.

you can do like this
DECLARE #likes AS TABLE(CName NVARCHAR(50), UName NVARCHAR(50), Rating INT)
insert into #likes (CName, UName, Rating) values
('Java', 'Klaus', '5'),
('Super', 'Klaus', '5'),
('MP', 'Klaus', '3'),
('Java', 'Marc', '5'),
('Mp', 'Marc', '5'),
('Super', 'Marc', '2'),
('Java', 'Nine', '2'),
('Super', 'Nine', '0'),
('MP', 'Karo', '3'),
('Super', 'Fabian', '4');
SELECT UName, COUNT(CName) Cnt FROM #Likes
WHERE Rating = (SELECT MAX(Rating) FROM #Likes)
GROUP BY UNAME
DEMO

Related

Using no aggregate function to get the top 5 for each customer [duplicate]

This question already has answers here:
How to SELECT the newest four items per category?
(8 answers)
Closed 3 days ago.
I would like to get the top 5 quantity for every customers from this table. I am able to get it through rank and row number, but I think there is a way using no aggregate function to achieve this.
DDL:
CREATE TABLE pd_orders (
`ordered_date` DATETIME,
`order_code` VARCHAR(15),
`customer_code` VARCHAR(14),
`product_name` VARCHAR(10),
`quantity` INTEGER
);
INSERT INTO pd_orders
(`ordered_date`, `order_code`, `customer_code`, `product_name`, `quantity`)
VALUES
('2023/1/17', '662370230_FP_TW', '1676797_FP_TW', 'product_1', '10'),
('2023/1/17', '662370230_FP_TW', '1676797_FP_TW', 'product_2', '10'),
('2023/1/17', '662102654_FP_TW', '3794354_FP_TW', 'product_3', '8'),
('2023/1/17', '662513860_FP_TW', '3989950_FP_TW', 'product_4', '8'),
('2023/1/17', '662070842_FP_TW', '2384070_FP_TW', 'product_5', '5'),
('2023/1/17', '662097031_FP_TW', '8080834_FP_TW', 'product_6', '4'),
('2023/1/17', '662097031_FP_TW', '8080834_FP_TW', 'product_7', '4'),
('2023/1/17', '662025835_FP_TW', '1635359_FP_TW', 'product_8', '6'),
('2023/1/17', '662025835_FP_TW', '1635359_FP_TW', 'product_9', '4'),
('2023/1/17', '662025835_FP_TW', '1635359_FP_TW', 'product_10', '4'),
('2023/1/17', '662025835_FP_TW', '1635359_FP_TW', 'product_11', '4'),
('2023/1/17', '662177606_FP_TW', '4400774_FP_TW', 'product_12', '5'),
('2023/1/17', '662177606_FP_TW', '4400774_FP_TW', 'product_13', '5'),
('2023/1/17', '662177606_FP_TW', '4400774_FP_TW', 'product_14', '5'),
('2023/1/17', '662333911_FP_TW', '6798862_FP_TW', 'product_15', '4'),
('2023/1/17', '662333911_FP_TW', '6798862_FP_TW', 'product_16', '7'),
('2023/1/17', '662376770_FP_TW', '717440_FP_TW', 'product_17', '4'),
('2023/1/17', '662376770_FP_TW', '717440_FP_TW', 'product_18', '4'),
('2023/1/17', '662260058_FP_TW', '10822485_FP_TW', 'product_19', '4'),
('2023/1/17', '662260058_FP_TW', '10822485_FP_TW', 'product_20', '6'),
('2023/1/17', '662260058_FP_TW', '10822485_FP_TW', 'product_21', '5'),
('2023/1/17', '662201603_FP_TW', '2653694_FP_TW', 'product_22', '6');
Solution using Rank()
SELECT
customer_code,
product_name,
quantity,
RANK() OVER ( ORDER BY quantity DESC, product_name) AS product_rank
FROM
pd_orders
WHERE
MONTH(ordered_date) = 1
Solution 1:
SELECT
customer_code,
product_name,
quantity,
RANK() OVER ( ORDER BY quantity DESC, product_name) AS product_rank
FROM
pd_orders
WHERE
MONTH(ordered_date) = 1
Solution 2:
SELECT
customer_code,
product_name,
quantity,
ROW_NUMBER() OVER ( ORDER BY quantity DESC, product_name) AS product_rank
FROM
pd_orders
WHERE
MONTH(ordered_date) = 1
Solution 3:
I kind of having idea that we can create the rank column by let the table's quantity compares with itself but no idea how to do it, below is code i tried but failed
select p1.customer_code,
p1.quantity,
count(p2.quantity) Sales_Rank
from pd_orders p1, pd_orders p2
where p2.quantity <= p1.quantity
group by p1.customer_code, p1.quantity
order by p1.quantity desc
I'm not quite clear about the reason why you don't want to use aggregate/window functions. But if you are using MySQL version 8.0, there is a way that could achieve your result without using these functions. We sort the data by customer_code, quantity DESC and using a SQL variable to calculate order rank per each customer. Then with that ranked data, you could apply filter on order rank to retrieve the result that you want to get.
SELECT
customer_code, product_name, quantity, order_rank
FROM
(SELECT
customer_code, product_name, quantity,
#rank := IF(#current_customer = customer_code, #rank + 1, 1) AS order_rank,
#current_customer := customer_code
FROM
pd_orders
WHERE
MONTH(ordered_date) = 1
ORDER BY customer_code, quantity DESC) ranked_data
WHERE order_rank <= 5;
You could see demo here.

MySQL-Count consective number

Write a SQL query to find number position as well number and consective number count
CREATE TABLE Logs (
`Id` INTEGER,
`Num` INTEGER
);
INSERT INTO Logs
(`Id`, `Num`)
VALUES
('1', '1'),
('2', '1'),
('3', '1'),
('4', '2'),
('5', '1'),
('6', '2'),
('7', '2');
Prefere Return
StartId Num Count
1 1 3
4 2 1
5 1 1
6 2 2
and also can i get any suggestion which function can be use with case function in MySQL Function
Looking at your data and expected results, I believe your expectations are inconsistent, eg you can either have 1 and 6 or 3 and 7.
What you need to do is group the data by successive num values and aggregate the results.
with gp as (
select *,
Row_Number() over(order by id)
- Row_Number() over(partition by num order by id) g
from logs
)
select Min(id) Id,
num, Count(*) Count
from gp
group by g, num
order by id

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

Age range computation where each country has at least two distinct ages of the individuals

For my dataset here, I want to create result such that for those countries (which have at least two distinct Ages), I could summarise the Age Range.
CREATE TABLE Employees(
ID int (3) NOT NULL,
Name varchar (50) NOT NULL,
Age int (3) NOT NULL,
Nationality varchar (50) NOT NULL
);
INSERT INTO Employees
(ID, Name, Age, Nationality)
VALUES
(1, 'CHIN YEN', '19', 'China'),
(2, 'MIKE PEARL', '21', 'United Kingdom'),
(3, 'GREEN FIELD', '45', 'Nethernalnds'),
(4, 'DEWANE PAUL', '57', 'Canada'),
(5, 'MATTS', '32', 'Australia'),
(6, 'PLANK OTO', '51', 'France'),
(7, 'Manish Kumar', '42', 'India'),
(8, 'Matts', '55', 'USA'),
(9, 'Mahesh Kumar', '32', 'USA'),
(10, 'Chin Yen', '21', 'Japan');
And what I was trying to do is:
SELECT Nationality,
Max(Age) - Min(Age) AS Age_Range
FROM Employees;
I think you just need a group by:
SELECT Nationality,
Max(Age) - Min(Age) AS Age_Range
FROM Employees
GROUP BY Nationality;
You might want to add HAVING Age_Range > 0.
To construct a query which will return age range for only those countries in which at least two individuals with distinct non-zero ages exist, following can be approach.
SELECT Nationality,
Max(Age) - Min(NULLIF(Age,0)) AS Age_Range
FROM Employees
GROUP BY Nationality
having Max(Age) - Min(NULLIF(Age,0)) > 0
what is does is that for any individual has age=0 , nullif convert its age into NULL which is then ignored by aggregate function MIN.
I have changed the data you shared as below.
INSERT INTO Employees
(ID, Name, Age, Nationality)
VALUES
(1, 'CHIN YEN', 0, 'United Kingdom'),
(2, 'MIKE PEARL', 21, 'United Kingdom'),
(3, 'GREEN FIELD', 45, 'Nethernalnds'),
(4, 'DEWANE PAUL', 57, 'Nethernalnds'),
(5, 'MATTS', 0, 'Nethernalnds'),
(6, 'PLANK OTO', 51, 'France'),
(7, 'Manish Kumar', 42, 'India'),
(8, 'Matts', 55, 'USA'),
(9, 'Mahesh Kumar', 32, 'USA'),
(10, 'Chin Yen', 21, 'Japan');
Below is the result as expected using the query i shared.
You can check DEMO here

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'