SQL:在一个字段中按最小值分组,同时选择不同的行


69

这就是我想要做的。假设我有这张桌子t:

key_id | id | record_date | other_cols
1      | 18 | 2011-04-03  | x
2      | 18 | 2012-05-19  | y
3      | 18 | 2012-08-09  | z
4      | 19 | 2009-06-01  | a
5      | 19 | 2011-04-03  | b
6      | 19 | 2011-10-25  | c
7      | 19 | 2012-08-09  | d

对于每个ID,我想选择包含最小record_date的行。所以我会得到:

key_id | id | record_date | other_cols
1      | 18 | 2011-04-03  | x
4      | 19 | 2009-06-01  | a

我看到的唯一解决方案是假定所有record_date条目都是不同的,但在我的数据中并非如此。使用带有两个条件的子查询和内部联接会给我一些ID的重复行,这是我不想要的:

key_id | id | record_date | other_cols
1      | 18 | 2011-04-03  | x
5      | 19 | 2011-04-03  | b
4      | 19 | 2009-06-01  | a

Answers:


91

怎么样:

SELECT mt.*     
FROM MyTable mt INNER JOIN
    (
        SELECT id, MIN(record_date) AS MinDate
        FROM MyTable
        GROUP BY id
    ) t ON mt.id = t.id AND mt.record_date = t.MinDate

这将获取每个ID的最短日期,然后基于这些值获取值。唯一的重复项是同一ID的最小record_dates是否重复。


1
嗯,起初我是使用表达式输出日期,这导致内部联接上的“ and”条件无法正常工作。将其更改为实际的列,并且现在可以正常运行(结果必须修改其他一些内容),谢谢!
user2765924 2013年

6
当存在两个具有相同ID和日期的记录时,这将不起作用,您会得到多行吗?
rajat

14

我可以通过这样做来达到您的预期结果

 SELECT id, min(record_date), other_cols 
  FROM mytable
  GROUP BY id

这对您有用吗?


出于某种原因,这似乎在人为的示例(sqlfiddle.com/#!2/f8469/6/0)中起作用,但是在实践中,我在选择列表中得到“ Column'database.table.col_name '无效,因为它不在聚合函数或GROUP BY子句中。” 无论如何,我能够使它与旁观者的答案一起工作,谢谢。
user2765924 2013年

1
是的,我遇到了同样的问题,我想在SQL Server上找到一个简单的答案
Pedro Braz 2015年

7

要获得每个类别中最便宜的产品,请在相关子查询中使用MIN()函数,如下所示:

    SELECT categoryid,
       productid,
       productName,
       unitprice 
    FROM products a WHERE unitprice = (
                SELECT MIN(unitprice)
                FROM products b
                WHERE b.categoryid = a.categoryid)

外部查询将扫描产品表中的所有行,并返回单价与相关子查询返回的每个类别中的最低价格匹配的产品。


2

我想在这里添加其他一些答案,如果您不需要第一个项目,但例如说第二个数字,则可以在子查询中使用rownumber并以此为基础得出结果集。

SELECT * FROM
(
    SELECT
        ROW_NUM() OVER (PARTITION BY Id ORDER BY record_date, other_cols) as rownum,
        *
    FROM products P
) INNER
WHERE rownum = 2

这还允许您在子查询中对多个列进行排序,如果两个record_dates具有相同的值,则可能会有所帮助。如果需要,您还可以将多个列划分为一个逗号


1

这样做很简单:

select t2.id,t2.record_date,t2.other_cols 
from (select ROW_NUMBER() over(partition by id order by record_date)as rownum,id,record_date,other_cols from MyTable)t2 
where t2.rownum = 1

0

这是一个老问题,但是对某人可能有用。在我来说,我不能使用子查询,因为我有一个大查询,并且我需要在结果上使用min(),如果我使用子查询,则数据库需要重新执行我的大查询查询。我正在使用Mysql

select t.* 
    from (select m.*, @g := 0
        from MyTable m --here i have a big query
        order by id, record_date) t
    where (1 = case when @g = 0 or @g <> id then 1 else  0 end )
          and (@g := id) IS NOT NULL

基本上,我对结果进行了排序,然后放置了一个变量,以便仅获取每个组中的第一条记录。


0

以下查询采用每个工作订单的第一个日期(在显示所有状态更改的表中):

SELECT
    WORKORDERNUM,
    MIN(DATE)
FROM
    WORKORDERS
WHERE
    DATE >= to_date('2015-01-01','YYYY-MM-DD')
GROUP BY
    WORKORDERNUM

0

如果record_date组内没有重复项:

认为它是过滤。MIN(record_date)从当前组简单地获取(WHERE)一()行:

SELECT * FROM t t1 WHERE record_date = (
                                 select MIN(record_date)
                                 from t t2 where t2.group_id = t1.group_id)

如果record_date一个小组中可能有2分钟以上的时间:

  1. 过滤掉非最小行(见上文)

  2. 然后(AND)record_date在给定的2+分钟行中仅选择一个group_id。例如,用最小唯一键选择一个:

                    AND key_id = (select MIN(key_id)
                                  from t t3 where t3.record_date = t1.record_date
                                              and t3.group_id    = t1.group_id)
    

所以

key_id | group_id | record_date | other_cols
1      | 18       | 2011-04-03  | x
4      | 19       | 2009-06-01  | a
8      | 19       | 2009-06-01  | e

将选择key_ids:#1和#4


0
select 
    department, 
    min_salary, 
    (select s1.last_name from staff s1 where s1.salary=s3.min_salary ) lastname 
from 
    (select department, min (salary) min_salary from staff s2 group by s2.department) s3

3
欢迎使用堆栈溢出。不鼓励在堆栈溢出中使用仅代码的答案,因为它们没有解释它如何解决问题。请编辑您的答案,以解释此代码的作用以及如何改善此问题已存在的现有答案,以便它对遇到类似问题的其他用户很有用。
FluffyKitten
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.