针对"按某一字段分组取最大(小)值所在行的数据"一文的深入讨论和研究

本文是对'按某一字段分组取最大(小)值所在行的数据'的深入讨论,主要关注第七种查询情况。文章分析了在无主键情况下,根据name分组并选取val最值的问题,提出除了临时表和row_number函数外的第三种解决方案,即使用相关子查询和TOP语句。此外,还增加了处理相同name和val但memo不同的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前天, 看到"爱新觉罗.毓华"的一篇关于讨论SQL查询的帖子: 按某一字段分组取最大(小)值所在行的数据

这篇文章讨论了七个查询情况, 除了第7部分外内容外, 其余的介绍的比较详细.  我继续针对第7种查询进行深入的讨论.

我们首先来看一下我们需要研究的数据情况:

create   table  tb(name  varchar ( 10 ),val  int ,memo  varchar ( 20 ))
insert   into  tb  values ( ' a ' ,     2 ,    ' a2(a的第二个值) ' )
insert   into  tb  values ( ' a ' ,     1 ,    ' a1--a的第一个值 ' )
insert   into  tb  values ( ' a ' ,     1 ,    ' a1--a的第一个值 ' )
insert   into  tb  values ( ' a ' ,     3 ,    ' a3:a的第三个值 ' )
insert   into  tb  values ( ' a ' ,     3 ,    ' a3:a的第三个值 ' )
insert   into  tb  values ( ' b ' ,     1 ,    ' b1--b的第一个值 ' )
insert   into  tb  values ( ' b ' ,     3 ,    ' b3:b的第三个值 ' )
insert   into  tb  values ( ' b ' ,     2 ,    ' b2b2b2b2 ' )
insert   into  tb  values ( ' b ' ,     4 ,    ' b4b4 ' )
insert   into  tb  values ( ' b ' ,     5 ,    ' b5b5b5b5b5 ' )

我们首先再来确认一下我们要做什么事情. 只有在清楚我们需要做什么事情的情况下, 我们才能得到正确的结论. 我们要根据某一个字段(例如: name), 进行分组, 并在分组的基础上, 选出某列(例如: val)上的最值. 并最终返回name, val值在表tb中所有字段信息.

由于表tb上没有主键, 因此name, val, memo这个三元组, 不一定能从表tb中返回唯一的元组. 由于我们要根据name进行分组, 并选出val上的最值, 我们可以发现name, val的二元组, 也不一定从表tb中返回唯一的元组.

根据"爱新觉罗.毓华"设计的用例(testcase)来看, 并没有考虑当name, val相同时, memo不同时应该返回何种结果. 通过"爱新觉罗.毓华"给出的求解方案, 我们知道无论memo为何值, 利用临时表或row_number都只返回"第一个"元组.

我们已经确认了"爱新觉罗.毓华"这位朋友的文章中第七种问题的情况, 我们除了要完成"爱新觉罗.毓华"已经完成的操作(问题1). 还要实现一个能够对不同memo返回多条结果的SQL语句(问题2). 实际上问题2比问题1要容易得多.

针对问题1来说, "爱新觉罗.毓华"给出2种方法分别利用:

1. 临时表+自增列;

2. row_number函数;

我们是否还能给出第三种方法呢? 在继续讨论问题1之前, 我们先来解决问题2, 问题2更加简单. 我们首先追加一些数据:

insert   into  tb  values ( ' a ' ,     1 ,    ' 问题2 ADD BY Edengundam ' )
insert   into  tb  values ( ' b ' ,     1 ,    ' 问题2 ADD BY Edengundam ' )

我们只对a, b加上两条数据, 来补充一下测试用例(我们不再讨论NULL值问题).

我们首先根据name字段进行分组, 并取出val中最小值:

SELECT  name,  MIN (val)  AS  minval  FROM  tb  GROUP   BY  name;

输出结果:

name       minval
---------- -----------
a          1
b          1

(2 row(s) affected)

接下来, 我们只需要利用 "相关子查询" 或者 "JOIN" 来选出最小值对应的元组:

SELECT   DISTINCT  tb.name, tb.val, tb.memo
FROM
tb,
(
SELECT  name,  MIN (val)  AS  minval  FROM  tb  GROUP   BY  name)  AS  tmp
WHERE  tb.name  =  tmp.name  AND  tb.val  =  tmp.minval;

输出结果:

name       val         memo
---------- ----------- ----------------------------------------
a          1           a1--a的第一个值
a          1           问题2 ADD BY Edengundam
b          1           b1--b的第一个值
b          1           问题2 ADD BY Edengundam

(4 row(s) affected)

注意, 这里的DISTINCT用来将重复的a, 1, 'a1--a的第一个值'列消去. 现在我们已经完成了问题2, 下面我们需要思考如何能够不依赖临时表和row_number这类函数来针对不同的name, MIN(val)返回一条元组.

我们应该从tb表中选择出name, MIN(val)的列, 并且这一列的memo是通过TOP语句唯一选择的. 这就是我们的思路, 利用相关子查询, TOP语句:

SELECT   DISTINCT  tb.name, tb.val, tb.memo
FROM
tb,
(
SELECT  name,  MIN (val)  AS  minval  FROM  tb  GROUP   BY  name)  AS  tmp
WHERE  tb.name  =  tmp.name
    
AND  tb.val  =  tmp.minval
    
AND  tb.memo  =  ( SELECT   TOP   1  memo  FROM  tb  AS  d  WHERE  tb.name  =  d.name  AND  tb.val  =  d.val);

我们看看输出结果:

name       val         memo
---------- ----------- ----------------------------------------
a          1           a1--a的第一个值
b          1           b1--b的第一个值

(2 row(s) affected)

我们已经完成任务了, 但是我必须提醒大家效率上来说, 最好的是"爱新觉罗.毓华"利用row_number函数实现的办法. 我的讨论只是帮助大家开头思路, 并且将原有讨论情况进行扩充.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值