MySQL从每个类别中选择前N名最便宜的产品

时间:2019-10-10 19:33:40

标签: mysql sql sorting group-by greatest-n-per-group

这是mySQL 产品

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

必填结果在每个类别中前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 个答案:

答案 0 :(得分:2)

您可以使用变量来获取每个类别的产品排名:

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

请参见demo
对于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  |

答案 1 :(得分:0)

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 | 
+--------+----------+-------+

答案 2 :(得分:0)

  

在@forpas答案下的评论中引用

     

您的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似乎不支持窗口功能。

  

但是尝试升级到不支持window的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  |

请参阅demo

注意:此查询需要

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

此索引要求可能有点过高,因为这会使索引文件比表文件大很多。假设Product | Price | Cat是唯一的列。

如果产品在猫和价格组中是唯一的,您应该可以摆脱。

查询

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  |

请参阅demo

注意:该查询仅需

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  |

请参阅demo

无论如何,有很多道路通向罗马,现在找到正确的道路