分组之后排序取第一条

oracle 分组后取每组第一条数据

 SELECT *       
    FROM (SELECT ROW_NUMBER() OVER(PARTITION BY x ORDER BY y DESC) rn,       
          test1.* FROM test1)       
   WHERE rn = 1

关联查询

今天关联查询遇到一个有趣的现象。

以前遇到过没整理,记录一下,方便以后查阅。

本文测试环境:

MySQL   5.7.17

前车之鉴

sql 之 left join、right join、inner join 的区别

一言以蔽之

left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 

right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录

inner join(等值连接) 只返回两个表中联结字段相等的行

数据准备

  • create table
CREATE TABLE mapping
(
	id           BIGINT AUTO_INCREMENT
	COMMENT '唯一标识'
		PRIMARY KEY,
	code         INT                                 NOT NULL
	COMMENT '错误编码',
	msg          VARCHAR(64)                         NOT NULL
	COMMENT '错误描述',
	created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
	COMMENT '创建时间'
)
	COMMENT '错误码映射表'
	
CREATE TABLE result
(
  id           BIGINT AUTO_INCREMENT
  COMMENT '唯一标识'
    PRIMARY KEY,
  code         INT                                 NOT NULL
  COMMENT '错误编码',
  created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
  COMMENT '创建时间'
)
  COMMENT '结果表'
  • init data
INSERT INTO result (code) VALUES (1), (2), (3);

INSERT INTO mapping (code, msg) VALUES (3, '错误3'), (4, '错误4');
  • query data
mysql> SELECT * FROM mapping;
+----+------+---------+---------------------+
| id | code | msg     | created_time        |
+----+------+---------+---------------------+
|  1 |    3 | 错误3   | 2017-11-20 10:24:57 |
|  2 |    4 | 错误4   | 2017-11-20 10:24:57 |
+----+------+---------+---------------------+
2 rows in set (0.00 sec)

mysql> SELECT * FROM result;
+----+------+---------------------+
| id | code | created_time        |
+----+------+---------------------+
|  1 |    1 | 2017-11-20 10:23:15 |
|  2 |    2 | 2017-11-20 10:23:15 |
|  3 |    3 | 2017-11-20 10:23:15 |
+----+------+---------------------+

LEFT JOIN

SELECT result.id, result.code, mapping.msg
FROM result
LEFT JOIN mapping
ON result.code = mapping.code;
  • 查询结果
+----+------+---------+
| id | code | msg     |
+----+------+---------+
|  3 |    3 | 错误3   |
|  1 |    1 | NULL    |
|  2 |    2 | NULL    |
+----+------+---------+

RIGHT JOIN

SELECT result.id, result.code, mapping.msg
FROM result
RIGHT JOIN mapping
ON result.code = mapping.code;
  • 查询结果
+------+------+---------+
| id   | code | msg     |
+------+------+---------+
|    3 |    3 | 错误3   |
| NULL | NULL | 错误4   |
+------+------+---------+

INNER JOIN

SELECT result.id, result.code, mapping.msg
FROM result
INNER JOIN mapping
ON result.code = mapping.code;
  • 查询结果
+----+------+---------+
| id | code | msg     |
+----+------+---------+
|  3 |    3 | 错误3   |
+----+------+---------+

1:n

当然上面都是基础回顾,下面才进入正题。

演示

如果我们的错误码映射表(mapping) 数据不是唯一的,会怎么样呢?

我们再次插入一条数据:

INSERT INTO mapping (code, msg) VALUES (3, '错误3');
  • mapping data
+----+------+---------+---------------------+
| id | code | msg     | created_time        |
+----+------+---------+---------------------+
|  1 |    3 | 错误3   | 2017-11-20 10:24:57 |
|  2 |    4 | 错误4   | 2017-11-20 10:24:57 |
|  3 |    3 | 错误3   | 2017-11-20 10:39:15 |
+----+------+---------+---------------------+
  • left join

这次查询的结果,结果出现了重复的情况。

mysql> SELECT result.id, result.code, mapping.msg
    -> FROM result
    -> LEFT JOIN mapping
    -> ON result.code = mapping.code;
+----+------+---------+
| id | code | msg     |
+----+------+---------+
|  3 |    3 | 错误3   |
|  3 |    3 | 错误3   |
|  1 |    1 | NULL    |
|  2 |    2 | NULL    |
+----+------+---------+

解决方式

group by

让 mapping 表对应的数据唯一即可

SELECT result.id, result.code, mapping.msg
FROM result
LEFT JOIN (SELECT code, msg FROM mapping GROUP BY code) AS mapping
ON result.code = mapping.code;

result:

+----+------+---------+
| id | code | msg     |
+----+------+---------+
|  1 |    1 | NULL    |
|  2 |    2 | NULL    |
|  3 |    3 | 错误3   |
+----+------+---------+

msg 没有包含在 GROUP BY 之中

同样的语句如果在 SQL SERVER 2008R2 中测试的话,可能会报错。可以按照实际业务使用聚合函数,比如:

SELECT result.id, result.code, mapping.msg
FROM result
LEFT JOIN (SELECT code, MAX(msg) AS msg FROM mapping GROUP BY code) AS mapping
ON result.code = mapping.code;

distinct

如果你只是想让结果保证唯一性,这样写会简洁一些:

注意:如果 mapping 在你的查询中,无法保证唯一性,则使用 GROUP BY 的方式。

SELECT DISTINCT result.id, result.code, mapping.msg
FROM result
LEFT JOIN mapping
ON result.code = mapping.code;

result:

+----+------+---------+
| id | code | msg     |
+----+------+---------+
|  3 |    3 | 错误3   |
|  1 |    1 | NULL    |
|  2 |    2 | NULL    |
+----+------+---------+