MySQL取每组的前N条记录

Posted 沧海一滴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL取每组的前N条记录相关的知识,希望对你有一定的参考价值。

 

mysql 分组后取每组前N条数据

与oracle的 rownumber() over(partition by xxx  order by xxx )语句类似,即:对表分组后排序

CREATE TABLE `mygoods` (  
  `goods_id` int(11) unsigned NOT NULL AUTO_INCREMENT,  
  `cat_id` int(11) NOT NULL DEFAULT 0,  
  `price` tinyint(3) NOT NULL DEFAULT 0,  
  `status` tinyint(3) DEFAULT 1,  
  PRIMARY KEY (`goods_id`),  
  KEY `icatid` (`cat_id`)  
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;  
  
INSERT INTO `mygoods` VALUES (1, 101, 90, 0);  
INSERT INTO `mygoods` VALUES (2, 101, 99, 1);  
INSERT INTO `mygoods` VALUES (3, 102, 98, 0);  
INSERT INTO `mygoods` VALUES (4, 103, 96, 0);  
INSERT INTO `mygoods` VALUES (5, 102, 95, 0);  
INSERT INTO `mygoods` VALUES (6, 102, 94, 1);  
INSERT INTO `mygoods` VALUES (7, 102, 93, 1);  
INSERT INTO `mygoods` VALUES (8, 103, 99, 1);  
INSERT INTO `mygoods` VALUES (9, 103, 98, 1);  
INSERT INTO `mygoods` VALUES (10, 103, 97, 1);  
INSERT INTO `mygoods` VALUES (11, 104, 96, 1);  
INSERT INTO `mygoods` VALUES (12, 104, 95, 1);  
INSERT INTO `mygoods` VALUES (13, 104, 94, 1);  
INSERT INTO `mygoods` VALUES (15, 101, 92, 1);  
INSERT INTO `mygoods` VALUES (16, 101, 93, 1);  
INSERT INTO `mygoods` VALUES (17, 101, 94, 0);  
INSERT INTO `mygoods` VALUES (18, 102, 99, 1);  
INSERT INTO `mygoods` VALUES (19, 105, 85, 1);  
INSERT INTO `mygoods` VALUES (20, 105, 89, 0);  
INSERT INTO `mygoods` VALUES (21, 105, 99, 1);  

说明:

表mygoods为商品表,cat_id为分类id,goods_id为商品id,status为商品当前的状态位(1:有效,0:无效)。

需求:每个分类下,找出两个价格最高的有效的商品。

1.每个分类找出价格最高的两个商品

mysql> select a.*   
    -> from mygoods a   
    -> where (select count(*) 
    -> from mygoods 
    -> where cat_id = a.cat_id and price > a.price  ) <2 
    -> order by a.cat_id,a.price desc;

SQL解析:
cat_id = a.cat_id  --是为了确定分组字段
price > a.price  --确定排序条件,由于是大于,则最大出现0次,第二大的出现1次,所以count(*)<2


+----------+--------+-------+--------+
| goods_id | cat_id | price | status |
+----------+--------+-------+--------+
|        2 |    101 |    99 |      1 |
|       17 |    101 |    94 |      0 |
|       18 |    102 |    99 |      1 |
|        3 |    102 |    98 |      0 |
|        8 |    103 |    99 |      1 |
|        9 |    103 |    98 |      1 |
|       11 |    104 |    96 |      1 |
|       12 |    104 |    95 |      1 |
|       21 |    105 |    99 |      1 |
|       20 |    105 |    89 |      0 |
+----------+--------+-------+--------+
10 rows in set (0.00 sec)

 

2.每个分类找出价格最高的有效的两个商品(正确)

mysql> select a.* 
    -> from mygoods a 
    -> where (select count(*) from mygoods 
    -> where cat_id = a.cat_id and price > a.price and status=1  ) <2 
    -> and status=1 
    -> order by a.cat_id,a.price desc ;
+----------+--------+-------+--------+
| goods_id | cat_id | price | status |
+----------+--------+-------+--------+
|        2 |    101 |    99 |      1 |
|       16 |    101 |    93 |      1 |
|       18 |    102 |    99 |      1 |
|        6 |    102 |    94 |      1 |
|        8 |    103 |    99 |      1 |
|        9 |    103 |    98 |      1 |
|       11 |    104 |    96 |      1 |
|       12 |    104 |    95 |      1 |
|       21 |    105 |    99 |      1 |
|       19 |    105 |    85 |      1 |
+----------+--------+-------+--------+
10 rows in set (0.00 sec)
3.每个分类找出价格最高的有效的两个商品(正确)
mysql> select a.* 
    -> from mygoods a 
    -> left join mygoods b 
    -> on a.cat_id = b.cat_id and a.price < b.price and b.status=1
    -> where a.status=1
    -> group by a.goods_id,a.cat_id,a.price
    -> having count(b.goods_id) < 2
    -> order by a.cat_id,a.price desc;
+----------+--------+-------+--------+
| goods_id | cat_id | price | status |
+----------+--------+-------+--------+
|        2 |    101 |    99 |      1 | 
|       16 |    101 |    93 |      1 | 
|       18 |    102 |    99 |      1 | 
|        6 |    102 |    94 |      1 | 
|        8 |    103 |    99 |      1 | 
|        9 |    103 |    98 |      1 | 
|       11 |    104 |    96 |      1 | 
|       12 |    104 |    95 |      1 | 
|       21 |    105 |    99 |      1 | 
|       19 |    105 |    85 |      1 | 
+----------+--------+-------+--------+
10 rows in set (0.00 sec)
4.每个分类找出价格最高的有效的两个商品(错误)
mysql> select a.* 
    -> from mygoods a 
    -> where (select count(*) from mygoods 
    -> where cat_id = a.cat_id and price > a.price  ) <2 and status=1 
    -> order by a.cat_id,a.price desc;
+----------+--------+-------+--------+
| goods_id | cat_id | price | status |
+----------+--------+-------+--------+
|        2 |    101 |    99 |      1 |
|       18 |    102 |    99 |      1 |
|        8 |    103 |    99 |      1 |
|        9 |    103 |    98 |      1 |
|       11 |    104 |    96 |      1 |
|       12 |    104 |    95 |      1 |
|       21 |    105 |    99 |      1 |
+----------+--------+-------+--------+
7 rows in set (0.00 sec)
由上可知,如果需要增加条件的话,需要在两处增加条件。
 

可以将每个分组下的goods_id合并。

mysql> select cat_id,GROUP_CONCAT(goods_id) from mygoods group by cat_id;
+--------+------------------------+
| cat_id | GROUP_CONCAT(goods_id) |
+--------+------------------------+
|    101 | 1,2,15,16,17           |
|    102 | 3,5,6,7,18             |
|    103 | 4,8,9,10               |
|    104 | 11,12,13               |
|    105 | 19,20,21               |
+--------+------------------------+
5 rows in set (0.00 sec)

https://blog.csdn.net/come_on_air/article/details/72902592

MySQL取每组的前N条记录:使用自连接的方式

 

一、对分组的记录取前N条记录:例子:取前 2条最大(小)的记录

1.用子查询: SELECT
    *
FROM
    right2 a
WHERE
    2 > (
        SELECT
            COUNT(*)
        FROM
            right2 b
        WHERE
            b.id = a.id
        AND b.account > a.account
    )
ORDER BY
    a.id,
    a.account DESC 2.用exists半连接: SELECT
        *
    FROM
        right2 a
    WHERE
        EXISTS (
            SELECT
                COUNT(*)
            FROM
                right2 b
            WHERE
                b.id = a.id
            AND a.account < b.account
            HAVING
                COUNT(*) < 2
        )
    ORDER BY
        a.id,
        a.account DESC 同理可以取组内最小的N条记录: SELECT
            *
        FROM
            right2 a
        WHERE
            2 > (
                SELECT
                    COUNT(*)
                FROM
                    right2 b
                WHERE
                    b.id = a.id
                AND b.account < a.account
            )
        ORDER BY
            a.id,
            a.account DESC 用exists: SELECT
                *
            FROM
                right2 a
            WHERE
                EXISTS (
                    SELECT
                        COUNT(*)
                    FROM
                        right2 b
                    WHERE
                        b.id = a.id
                    AND a.account > b.account
                    HAVING
                        COUNT(*) < 2
                )
            ORDER BY
                a.id,
                a.account DESC SQLServer支持top - N: SELECT
                    a.*
                FROM
                    tb a
                WHERE
                    val = (
                        SELECT
                            top 3 val
                        FROM
                            tb
                        WHERE
                            NAME = a. NAME
                    )
                ORDER BY
                    a. NAME

 

如果取每组的最大(小)一条记录我常用:

SELECT
    id,
    val
FROM
    t b
INNER JOIN (
    SELECT
        *
    FROM
        t a
    WHERE

    ORDER BY
        val DESC
) a ON a.id = b.id
GROUP BY
    a.id
ORDER BY
    id;

 

二.实例:取每组最大的前 N条          

CREATE TABLE t2 (
    id INT PRIMARY KEY,
    gid CHAR,
    col1 INT,
    col2 INT
) ENGINE = INNODB;

INSERT INTO t2
VALUES
    (1, A, 31, 6),
    (2, B, 25, 83),
    (3, C, 76, 21),
    (4, D, 63, 56),
    (5, E, 3, 17),
    (6, A, 29, 97),
    (7, B, 88, 63),
    (8, C, 16, 22),
    (9, D, 25, 43),
    (10, E, 45, 28),
    (11, A, 2, 78),
    (12, B, 30, 79),
    (13, C, 96, 73),
    (14, D, 37, 40),
    (15, E, 14, 86),
    (16, A, 32, 67),
    (17, B, 84, 38),
    (18, C, 27, 9),
    (19, D, 31, 21),
    (20, E, 80, 63),
    (21, A, 89, 9),
    (22, B, 15, 22),
    (23, C, 46, 84),
    (24, D, 54, 79),
    (25, E, 85, 64),
    (26, A, 87, 13),
    (27, B, 40, 45),
    (28, C, 34, 90),
    (29, D, 63, 8),
    (30, E, 66, 40),
    (31, A, 83, 49),
    (32, B, 4, 90),
    (33, C, 81, 7),
    (34, D, 11, 12),
    (35, E, 85, 10),
    (36, A, 39, 75),
    (37, B, 22, 39),
    (38, C, 76, 67),
    (39, D, 20, 11),
    (40, E, 81, 36);

 

取每组gid 最大的前N条记录:使用自连接或则半连接:

*N=1时:

自连接:降序排好后group by取每组最大的一条。

SELECT
    *
FROM
    (
        SELECT
            *
        FROM
            t2
        ORDER BY
            col2 DESC
    ) AS a
GROUP BY
    gid
ORDER BY
    gid;

 

半连接方式:找不到比最大值还大的。

SELECT
    *
FROM
    t2 a
WHERE
    NOT EXISTS (
        SELECT
            1
        FROM
            t2 b
        WHERE
            b.gid = a.gid
        AND b.col2 > a.col2
    )
ORDER BY
    a.gid;

 

 

*N=3时:

自连接:

SELECT
    *
FROM
    t2 a
WHERE
    3 > (
        SELECT
            count(*)
        FROM
            t2
        WHERE
            gid = a.gid
        AND col2 > a.col2
    )
ORDER BY
    a.gid,
    a.col2 DESC;

 

半连接:

SELECT
    *
FROM
    t2 a
WHERE
    EXISTS (
        SELECT
            count(*)
        FROM
            t2 b
        WHERE
            b.gid = a.gid
        AND a.col2 < b.col2
        HAVING
            (count(*)) < 3
    )
ORDER BY
    a.gid,
    a.col2 DESC

Oracle取每组的前N条记录:可以使用分析函数,hive中也能使用

SELECT * FROM(
SELECT z.type , z.code ,ROW_NUMBER()
OVER(PARTITION BY z.type ORDER BY z.code desc) AS code_id
FROM group_info z
)
WHERE code_id <4;   #取每组最大的前四条记录

https://blog.csdn.net/kuodannie1668/article/details/79756964

 

以上是关于MySQL取每组的前N条记录的主要内容,如果未能解决你的问题,请参考以下文章

Oracle分组查询取每组排序后的前N条记录

Mysql 分组以后取每组的前三名数据

sql数据库怎么实现分组并取每组的前1条语句,按日期排序?

oracle开展分组后,取出每组的前几条数据

mysql使用GROUP BY分组实现取前N条记录的方法

MySQL 先按某字段分组,再取每组中前N条记录