【问题标题】:How to return 1 row for each distinct value in a given column in Oracle? [duplicate]如何为 Oracle 中给定列中的每个不同值返回 1 行? [复制]
【发布时间】:2016-10-18 12:46:38
【问题描述】:

我的数据看起来像

NBR  ID  DT
1    1   01-DEC-01 
1    2   01-JAN-01
2    3   01-JAN-01
2    4   O2-JAN-01

我只想为每个NBR 获取一行,并带有最早的日期。 所以我希望结果是

NBR  ID  DT
1    2   01-JAN-01
2    3   01-JAN-01

但是,我不能使用 LIMIT,因为我使用的是 Oracle。我尝试了以下方法,但没有给出我期望的结果

select DISTINCT NBR, ID, DT
from tablename
group by NBR, ID, DT
order by DT

编辑:这不是该链接的副本,因为我是按 NBR 分组的。我希望每个不同的 NBR 有一行。

【问题讨论】:

  • 重复! :) stackoverflow.com/a/26051830/6049590 应该正是他搜索的内容
  • 这不是该链接的副本,因为我是按 NBR 分组的。我想为每个不同的 NBR 设置一行。

标签: sql oracle greatest-n-per-group sql-limit


【解决方案1】:

解决方案与limitorder by 无关,Oracle 与否。您想使用row_number 解析函数轻松解决此问题:

select nbr, id, dt
  from (select nbr, id, dt,
               row_number() over (partition by nbr order by dt) as rn
          from tbl)
 where rn = 1

【讨论】:

    【解决方案2】:

    你也可以使用the FIRST analytic function:

    select NBR,
      min(ID) keep (dense_rank first order by DT) as ID,
      min(DT) keep (dense_rank first order by DT) as DT
    from tablename
    group by NBR
    order by DT;
    
           NBR         ID DT                
    ---------- ---------- -------------------
             2          3 2001-01-01 00:00:00
             1          2 2001-01-01 00:00:00
    

    【讨论】:

      【解决方案3】:

      您可以将KEEP ( DENSE_RANK FIRST ...) 与聚合函数一起使用:

      Oracle 设置

      CREATE TABLE table_name ( NBR, ID, DT ) AS
      SELECT 1, 1, DATE '2001-12-01' FROM DUAL UNION ALL 
      SELECT 1, 2, DATE '2001-01-01' FROM DUAL UNION ALL
      SELECT 2, 3, DATE '2001-01-01' FROM DUAL UNION ALL
      SELECT 2, 4, DATE '2001-01-02' FROM DUAL;
      

      查询

      SELECT NBR,
             MAX( ID ) KEEP ( DENSE_RANK FIRST ORDER BY DT ) AS ID,
             MIN( DT ) AS DT
      FROM   table_name
      GROUP BY NBR;
      

      输出

             NBR         ID DT                
      ---------- ---------- -------------------
               1          2 2001-01-01 00:00:00 
               2          3 2001-01-01 00:00:00 
      

      【讨论】:

        【解决方案4】:
        SELECT t.*
        FROM
            tablename t
            INNER JOIN 
            (
               SELECT
                  NBR
                  ,MIN(DT) AS DT
               FROM
                  tablename
               GROUP BY
                  NBR
            ) g
            ON t.NBR = g.NBR
            AND t.DT = g.DT
        

        我和这个网站上的其他人一样喜欢窗口功能和 ctes。这是一种老式的方式,不使用这些方式来娱乐。请注意,如果 NBR 最早有重复的 DT,您将获得该 NBR 的多行

        如果最早 DTNBR 重复,您可以进行另一个级别的聚合,这样会变得更加疯狂。在下一个级别,您将获得min(ID),然后将其关联回表格。如果窗口函数是一个选项,它们确实是一个更好的选择............

        SELECT t1.*
        FROM
            tablename t1
            INNER JOIN 
               (
                SELECT t.NBR, MIN(t.ID) AS ID
                FROM
                     tablename t2
                     INNER JOIN 
                     (
                        SELECT
                          NBR
                          ,MIN(DT) AS DT
                        FROM
                          tablename t3
                        GROUP BY
                          NBR
                     ) g
                     ON t.NBR = g.NBR
                     AND t.DT = g.DT
                GROUP BY
                  t.NBR
               ) m
            ON t1.NBR = m.NBR
            AND t1.ID = m.ID
        

        【讨论】:

        • 但如果 NBR 的早期 DT 存在重复,我只希望它返回一行
        • row_number 窗口函数是最好的,因为它不是一个选项。如果它们与最低 id 重复,您将如何选择哪条记录?
        • @user6347191 如果您真的不想通过窗口函数执行此操作,请查看我的答案底部,当每个 NBR 存在重复的最早 DT 时,您将看到任意仅获得 1 条记录跨度>
        • DT = MIN(DT) 是无效的 SQL。另外:Oracle 实际上是第一个支持窗口函数的 DBMS。从第 8 版开始就有了
        • @a_horse_with_no_name 感谢您的提示我基本上不写 oracle 语法,我将 min(id) 更改为 id,将 min(dt) 更改为 dt。我只是写了一个疯狂的答案,因为用户似乎厌恶窗口函数,但我很高兴知道 oracle 是第一个支持的!
        猜你喜欢
        • 1970-01-01
        • 2019-05-15
        • 1970-01-01
        • 2020-11-17
        • 1970-01-01
        • 1970-01-01
        • 2014-10-08
        • 2012-01-27
        • 2020-09-23
        相关资源
        最近更新 更多