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

Posted

技术标签:

【中文标题】SQL:在选择不同行时按一个字段中的最小值分组【英文标题】:Group by minimum value in one field while selecting distinct rows 【发布时间】:2013-09-14 12:46:05 【问题描述】:

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

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,我想选择包含最小记录日期的行。所以我会得到:

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

【问题讨论】:

如果有 min_by 函数,请考虑使用它。它使我免于编写更复杂的东西。 【参考方案1】:

比如:

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 的最小记录日期重复。

【讨论】:

啊,最初我使用表达式来输出导致内部连接上的“和”条件无法正常工作的日期。将其更改为实际列,现在可以使用(因此不得不修改其他一些内容),谢谢! 当存在两条相同 id 和 date 的记录时,这将不起作用,会得到多行吗?【参考方案2】:

我可以通过在mysql 中执行此操作来达到您的预期结果:

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

这对你有用吗?

【讨论】:

无论出于何种原因,这似乎在人为的示例(sqlfiddle.com/#!2/f8469/6/0)中有效,但实际上我得到“列'database.table.col_name'在选择列表中无效,因为它不是包含在聚合函数或 GROUP BY 子句中。”无论如何,我都能让它与 asstander 的答案一起工作,谢谢。 是的,我遇到了同样的问题,我想在 SQL Server 上得到一个像这样的简单答案【参考方案3】:

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

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

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

【讨论】:

【参考方案4】:

我想在这里添加一些其他答案,如果您不需要 第一个 项,但说第二个数字,例如您可以在子查询中使用 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

这还允许您对子查询中的多个列进行排序,如果两个记录日期具有相同的值,这可能会有所帮助。如果需要,您还可以使用逗号分隔多个列

【讨论】:

【参考方案5】:

这样做很简单:

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

【讨论】:

【参考方案6】:
SELECT p.* FROM tbl p
INNER JOIN(
  SELECT t.id, MIN(record_date) AS MinDate
  FROM tbl t
  GROUP BY t.id
) t ON p.id = t.id AND p.record_date = t.MinDate
GROUP BY p.id

此代码消除了重复的record_date,以防有相同的ids 和相同的record_date。 如果要重复,请删除最后一行 GROUP BY p.id

【讨论】:

【参考方案7】:

这是一个老问题,但这对某人有用 在我的情况下,我不能使用子查询,因为我有一个大查询,我需要在我的结果上使用 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

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

【讨论】:

【参考方案8】:

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

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

【讨论】:

【参考方案9】:

如果record_date 在组内没有重复项:

将其视为过滤。简单地从当前组中获取 (WHERE) 一个 (MIN(record_date)) 行:

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

如果一个组内可能有 2+ 分钟 record_date

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

    然后 (AND) 在给定的group_id 内从至少 2+ 行 record_date 中选择一个。例如。选择具有最小唯一键的那个:

                    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

【讨论】:

【参考方案10】:
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

【讨论】:

欢迎来到 Stack Overflow。 Stack Overflow 上不鼓励仅使用代码的答案,因为它们没有解释它是如何解决问题的。请编辑您的答案以解释此代码的作用以及它如何改进此问题已有的现有答案,以便对其他有类似问题的用户有用。

以上是关于SQL:在选择不同行时按一个字段中的最小值分组的主要内容,如果未能解决你的问题,请参考以下文章

如何在表的一个字段中按最小值分组,保留同一行中的所有值?

如何在 SQL 中分组和选择最小值

按列分组并选择具有多个最小值的行中的所有字段

【SQL】根据一个字段分组求另一个字段的最大值,并带出其他字段

SQL查询以选择具有最小值的不同行

SQL Oracle - 按 ID、任务 ID、最小和最大时间戳分组