I have a tableX with items and each item has a category_id and rating. I want to take the item with the highest rating form each category.
I imagine i have to do something like
SELECT DISTINCT category_id from tableX;
Then run a foreach those results with another query like
SELECT * from tableX where category_id = ${1} ORDER BY rating, LIMIT 1;
OR something like
call foreach('SELECT distinct category_id FROM tableX', 'SELECT * from tableX WHERE category_id = ${1} ORDER BY rating DESC LIMIT 1')
Edit:
I added some rows examples
CREATE TABLE `tableX` (
`id` bigint(20) UNSIGNED NOT NULL,
`rating` double(6,1) DEFAULT '0.0',
`category_id` int(10) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Dumping data for table tableX
INSERT INTO `tableX` (`id`, `rating`, `category_id`) VALUES
(1463, 8.0, 1),
(1464, 8.0, 1),
(1465, 8.0, 1),
(1466, 9.0, 1),
(1467, 9.0, 1),
(1468, 3.0, 1),
(1469, 8.0, 1),
(1470, 2.0, 1),
(1471, 4.0, 1),
(1472, 9.0, 1),
(1473, 5.0, 1),
(1474, 9.0, 1),
(1475, 10.0, 1),
(1476, 7.0, 1),
(1477, 7.0, 1),
(1478, 6.0, 1),
(1479, 10.0, 1),
(1480, 3.0, 1),
(1481, 7.0, 1),
(1482, 4.0, 1),
(1483, 7.0, 1),
(1484, 4.0, 1),
(1485, 2.0, 1),
(1486, 4.0, 1),
(1487, 5.0, 1),
(1488, 9.0, 1),
(1489, 8.0, 1),
(1490, 7.0, 1),
(1491, 10.0, 1),
(1492, 9.0, 1),
(1493, 9.0, 1),
(1494, 9.0, 2),
(1495, 3.0, 2),
(1496, 9.0, 2),
(1497, 9.0, 2),
(1498, 2.0, 2),
(1499, 4.0, 2),
(1500, 5.0, 2),
(1501, 7.0, 2),
(1502, 5.0, 2),
(1503, 7.0, 2),
(1504, 10.0, 2),
(1505, 6.0, 2),
(1506, 6.0, 4),
(1507, 1.0, 4),
(1508, 5.0, 4),
(1509, 7.0, 5),
(1510, 4.0, 5);
Indexes for dumped tables
ALTER TABLE `tableX`
ADD PRIMARY KEY (`id`),
ADD KEY `category_id` (`category_id`);
Use rank()
or row_number()
:
select x.*
from (select x.*,
row_number() over (partition by category_id order by rating desc) as seqnum
from tableX x
) x
where seqnum = 1;
For ties, use rank()
(that is all rows with the highest rating).
That above works in MySQL 8+. In earlier versions, you can use a correlated subquery. Something like:
select x.*
from tableX x
where x.rating = (select max(x2.rating)
from tableX x2
where x2.category_id = x.category_id
);
If you want only one row using the second form, then:
select x.*
from tableX x
where x.id = (select x2.id
from tableX x2
where x2.category_id = x.category_id
order by x2.rating desc
limit 1
);