Oracle 取Group By 第一条
在 Oracle 中,当你使用GROUP BY
进行分组查询时,如果需要获取每组的第一条记录,可以使用以下几种常见的方法,下面将分别详细介绍。
方法一:使用ROW_NUMBER()
窗口函数
ROW_NUMBER()
是一个窗口函数,它可以为结果集中的每一行分配一个唯一的行号,你可以根据分组字段和排序字段来为每组内的记录编号,然后筛选出每组行号为 1 的记录。
示例表结构和数据
sql
-- 创建示例表
CREATE TABLE employees (
dept_id NUMBER,
emp_id NUMBER,
emp_name VARCHAR2(100),
salary NUMBER
);
-- 插入示例数据
INSERT INTO employees VALUES (1, 101, 'Alice', 5000);
INSERT INTO employees VALUES (1, 102, 'Bob', 6000);
INSERT INTO employees VALUES (2, 103, 'Charlie', 4500);
INSERT INTO employees VALUES (2, 104, 'David', 5500);
COMMIT;
查询语句
sql
SELECT *
FROM (
SELECT
dept_id,
emp_id,
emp_name,
salary,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY emp_id) as rn
FROM
employees
)
WHERE rn = 1;
代码解释
- 子查询部分:
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY emp_id)
:PARTITION BY dept_id
表示按照dept_id
进行分组,ORDER BY emp_id
表示在每个分组内按照emp_id
进行排序。ROW_NUMBER()
会为每个分组内的记录依次分配一个行号。
- 外层查询部分:
WHERE rn = 1
:筛选出行号为 1 的记录,即每组的第一条记录。
方法二:使用KEEP
函数(适用于聚合查询)
KEEP
函数可以在聚合函数中使用,用于根据指定的排序规则选择特定的值。
查询语句
sql
SELECT
dept_id,
MIN(emp_id) KEEP (DENSE_RANK FIRST ORDER BY emp_id) as emp_id,
MIN(emp_name) KEEP (DENSE_RANK FIRST ORDER BY emp_id) as emp_name,
MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY emp_id) as salary
FROM
employees
GROUP BY
dept_id;
代码解释
MIN(emp_id) KEEP (DENSE_RANK FIRST ORDER BY emp_id)
:在每个dept_id
分组内,按照emp_id
排序,选择排序后的第一条记录的emp_id
值。- 同样的逻辑适用于
emp_name
和salary
字段。
方法三:使用FETCH FIRST
(Oracle 12c 及以上版本)
如果只是想简单地获取每组的第一条记录,可以结合 GROUP BY
和子查询,然后使用 FETCH FIRST
来获取每组的第一条。
查询语句
sql
SELECT *
FROM (
SELECT
dept_id,
emp_id,
emp_name,
salary
FROM
employees
ORDER BY
dept_id, emp_id
)
GROUP BY
dept_id
HAVING
emp_id = MIN(emp_id)
FETCH FIRST 1 ROW WITH TIES;
代码解释
- 子查询部分:对
employees
表按照dept_id
和emp_id
进行排序。 - 外层查询部分:
GROUP BY dept_id
:按照dept_id
进行分组。HAVING emp_id = MIN(emp_id)
:筛选出每组中emp_id
最小的记录。FETCH FIRST 1 ROW WITH TIES
:获取每组的第一条记录,如果有并列的情况也会一并返回。
以上三种方法都可以实现获取 GROUP BY
分组后每组的第一条记录,你可以根据实际需求和数据库版本选择合适的方法。
mysql 取Group By 第一条
在 MySQL 中,若要获取 GROUP BY
分组后的每组第一条记录,有多种实现方式,以下为你详细介绍常见的几种方法。
示例表结构和数据
首先创建一个示例表并插入一些数据,后续的查询都基于这个表展开:
sql
-- 创建示例表
CREATE TABLE employees (
dept_id INT,
emp_id INT,
emp_name VARCHAR(100),
salary DECIMAL(10, 2),
PRIMARY KEY (emp_id)
);
-- 插入示例数据
INSERT INTO employees (dept_id, emp_id, emp_name, salary) VALUES
(1, 101, 'Alice', 5000.00),
(1, 102, 'Bob', 6000.00),
(2, 103, 'Charlie', 4500.00),
(2, 104, 'David', 5500.00);
方法一:使用 JOIN
和子查询
这种方法先通过子查询找出每组的最小 emp_id
,然后将原表与子查询结果进行连接,从而获取每组的第一条记录。
sql
SELECT e.*
FROM employees e
JOIN (
-- 子查询:找出每个部门的最小 emp_id
SELECT dept_id, MIN(emp_id) AS min_emp_id
FROM employees
GROUP BY dept_id
) sub ON e.dept_id = sub.dept_id AND e.emp_id = sub.min_emp_id;
代码解释:
- 子查询
SELECT dept_id, MIN(emp_id) AS min_emp_id FROM employees GROUP BY dept_id
会按照dept_id
分组,并找出每组中emp_id
最小的记录。 - 主查询将
employees
表(e
)与子查询结果(sub
)进行连接,连接条件是dept_id
相等且emp_id
等于子查询中找出的最小emp_id
,这样就能获取每组的第一条记录。
方法二:使用 ROW_NUMBER()
窗口函数(MySQL 8.0 及以上版本支持)
ROW_NUMBER()
窗口函数可以为结果集中的每一行分配一个唯一的行号,我们可以根据分组字段和排序字段为每组内的记录编号,然后筛选出行号为 1 的记录。
sql
SELECT *
FROM (
-- 子查询:为每个部门的记录分配行号
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY emp_id) AS rn
FROM
employees
) sub
WHERE rn = 1;
代码解释:
- 子查询中的
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY emp_id)
会按照dept_id
进行分组,在每个分组内按照emp_id
排序,并为每行分配一个行号。 - 主查询通过
WHERE rn = 1
筛选出行号为 1 的记录,即每组的第一条记录。
方法三:使用 EXISTS
子查询
此方法通过 EXISTS
子查询来判断当前记录是否为每组的第一条记录。
sql
SELECT e1.*
FROM employees e1
WHERE NOT EXISTS (
-- 子查询:判断是否存在 emp_id 更小且 dept_id 相同的记录
SELECT 1
FROM employees e2
WHERE e2.dept_id = e1.dept_id AND e2.emp_id < e1.emp_id
);
代码解释:
- 对于
employees
表中的每一条记录e1
,子查询会检查是否存在另一条记录e2
,其dept_id
与e1
相同,但emp_id
更小。 - 如果不存在这样的记录,说明
e1
是该组中emp_id
最小的记录,即每组的第一条记录,会被主查询返回。
以上三种方法各有优劣,你可以根据实际的 MySQL 版本和数据情况选择合适的方法来获取 GROUP BY
分组后的每组第一条记录。