1. 程式人生 > >MySQL取每組的前N條記錄

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