MySQL Выбрать top N Самый дешевый продукт из каждой категории - PullRequest
0 голосов
/ 10 октября 2019

Вот mySQL product table

+--------+-------+-----+
|Product | Price | Cat |
+--------+-------+-----+
| iPhone | 1     | 32  |
| Samsung| 2     | 32  |
| Dell   | 1     | 21  |
| HP     | 2     | 21  |
| RedMi  | 3     | 32  |
| Acer   | 3     | 21  |
+--------+-------+-----+

Требуемый результат top 2 самых дешевых в каждой категории:

+--------+-------+-----+
|Product | Price | Cat |
+--------+-------+-----+
| iPhone | 1     | 32  |
| Samsung| 2     | 32  |
| Dell   | 1     | 21  |
| HP     | 2     | 21  |
+--------+-------+-----+

Я пытался select * from products group by cat order by price, но он возвращает толькоПервый дешевый продукт. Мне нужен топ 2 самых дешевых.

Ответы [ 3 ]

2 голосов
/ 10 октября 2019

Вы можете использовать переменные, чтобы получить рейтинг продуктов для каждой категории:

SET @rn := 0;
SET @cat := 0;
SELECT product, price, cat FROM (
  SELECT @rn := case 
    WHEN @cat = cat then @rn + 1 
    ELSE 1
  END AS rn, product, price, cat,
  @cat := cat  
  FROM products 
  ORDER BY cat, price 
) t
WHERE rn <= 2
ORDER BY cat, rn

См. Демоверсию . Для MySQL 8.0+ есть ROW_NUMBER():

SELECT product, price, cat
FROM (
  SELECT *,
    ROW_NUMBER() OVER (PARTITION BY cat ORDER BY price) rn  
  FROM products
) t 
WHERE rn <= 2
ORDER BY cat, rn

См. demo . Результаты:

| product | price | cat |
| ------- | ----- | --- |
| Dell    | 1     | 21  |
| HP      | 2     | 21  |
| iPhone  | 1     | 32  |
| Samsung | 2     | 32  |
0 голосов
/ 10 октября 2019

Цитируется из комментария под @forpas answer

В синтаксисе SQL есть ошибка;проверьте руководство, соответствующее вашей версии сервера MariaDB, чтобы найти правильный синтаксис для использования рядом с '(PARTITION BY -

. Вы также можете попробовать смоделировать результаты

SELECT product, price, cat
FROM (
  SELECT *,
    ROW_NUMBER() OVER (PARTITION BY cat ORDER BY price) rn  
  FROM products
) t 
WHERE rn <= 2
ORDER BY cat, rn

Подробнееили меньше с запросами ниже, поскольку ваша MariaDB кажется не поддерживает оконные функции.

Но попробуйте обновить до версии MariaDB, которая поддерживает оконные функции.

Запрос

SELECT  
   products1.Product
 , products1.price
 , products1.cat 
FROM 
 products products1
LEFT JOIN
 products products2
ON
 products1.cat = products2.cat
AND 
  products1.price >= products2.price
GROUP BY 
   products1.Product
 , products1.cat
 , products1.price
HAVING
 COUNT(*) <= 2
ORDER BY
   products1.Product ASC
 , products1.cat ASC
 , products1.price ASC

Результат

| Product | price | cat |
| ------- | ----- | --- |
| Dell    | 1     | 21  |
| HP      | 2     | 21  |
| iPhone  | 1     | 32  |
| Samsung | 2     | 32  |

см. демо

Примечание: для этого запроса требуется

INDEX(Product, Cat, price), INDEX(Cat, price)

Это требование индекса может быть слишком большим, так как это сделает индексный файл (намного) больше, чем файл таблицы. 1037 * являются единственными столбцами ..

Если Товар является уникальным для кошки и ценовой группы, вы можете сойти с рук.

Запрос

SELECT  
   (SELECT
      products_inner.Product
    FROM
     products products_inner
    WHERE
     products_inner.cat = products1.cat 
    AND
     products_inner.price = products1.price 
   ) AS "Product"
 , products1.price
 , products1.cat 
FROM 
 products products1
LEFT JOIN
 products products2
ON
 products1.cat = products2.cat
AND 
  products1.price >= products2.price
GROUP BY 
   products1.cat
 , products1.price
HAVING
 COUNT(*) <= 2
ORDER BY
   products1.cat ASC 
 , products1.price ASC

Результат

| Product | price | cat |
| ------- | ----- | --- |
| Dell    | 1     | 21  |
| HP      | 2     | 21  |
| iPhone  | 1     | 32  |
| Samsung | 2     | 32  |

см. демо

Примечание: эта очередьry требуется только

INDEX(Cat, price)

Но, с другой стороны, он использует связанный подзапрос к сгруппированной записи, чтобы получить продукт, который может плохо оптимизироваться.

Обратите внимание на более короткий стандартный запрос ANSI / ISO SQL 92, который вы также можете написать и использовать, о котором я совсем забыл, в offcource также работает с INDEX(Cat, price)

    SELECT 
     *
    FROM 
     products products_outer
    WHERE 
     2 <= (

       SELECT 
          COUNT(*) FROM products products_inner
       WHERE
          products_inner.cat = products_outer.cat
        AND 
          products_inner.price >= products_outer.price 
     )
;

Результат

| Product | Price | Cat |
| ------- | ----- | --- |
| iPhone  | 1     | 32  |
| Samsung | 2     | 32  |
| Dell    | 1     | 21  |
| HP      | 2     | 21  |

см. демо

Как бы то ни было, многие дороги идут в Рим, теперь найдите правильную

0 голосов
/ 10 октября 2019
select type, variety, price
from fruits
where price = (select min(price) from fruits as f where f.type = fruits.type)
   or price = (select min(price) from fruits as f where f.type = fruits.type
      and price > (select min(price) from fruits as f2 where f2.type = fruits.type));
+--------+----------+-------+
| type   | variety  | price |
+--------+----------+-------+
| apple  | gala     |  2.79 | 
| apple  | fuji     |  0.24 | 
| orange | valencia |  3.59 | 
| orange | navel    |  9.36 | 
| pear   | bradford |  6.05 | 
| pear   | bartlett |  2.14 | 
| cherry | bing     |  2.55 | 
| cherry | chelan   |  6.33 | 
+--------+----------+-------+
...