【问题标题】:Tricky SQL SELECT Statement棘手的 SQL SELECT 语句
【发布时间】:2010-11-16 21:49:56
【问题描述】:

在我的项目中选择数据时遇到性能问题。

有一个表有 3 列:“id”、“time”和“group”

  • ID 像往常一样只是唯一的 ID。
  • 时间为条目的创建日期。
  • 该小组在那里将某些条目累积在一起。

所以表格数据可能是这样的:

ID | TIME      | GROUP
------------------------
1  | 20090805  | A
2  | 20090804  | A
3  | 20090804  | B
4  | 20090805  | B
5  | 20090803  | A
6  | 20090802  | B

...等等。

现在的任务是在给定日期的每个组中选择“当前”条目(它们的 ID)。也就是说,为每个组找到给定日期的最新条目。

以下前提条件适用:

  • 我事先不知道不同的群体 - 可能会有很多不同的群体随着时间的推移而变化
  • 选择日期可能位于表中条目的日期“之间”。然后我必须在每组中找到最接近的一个。也就是说,TIME 小于选择日期,但在组中适用此规则的日期中的最大值。

我目前所做的是一个多步骤过程,我想将其更改为单个 SELECT 语句:

  1. SELECT DISTINCT group FROM table 查找可用组
  2. 对于 1) 中找到的每个组,SELECT * FROM table WHERE time<selectionDate AND group=loop ORDER BY time DESC
  3. 取 2) 中找到的每个结果的第一行

显然这不是最优的。

因此,如果一些更有经验的 SQL 专家可以帮助我找到将这些步骤放在一个语句中的解决方案,我将非常高兴。

谢谢!

【问题讨论】:

  • 您使用的是什么数据库?更高级的功能差异很大。
  • TIMETABLEGROUP 是保留的 SQL 关键字,因此,我发现它们作为 SQL 对象(如表和列)的名称有点误导。
  • 首先,非常感谢迄今为止快速而出色的答案。我真的忘记了一些细节:我正在使用 ORACLE,但最好该解决方案也适用于 Postgresql。是的,这些名称确实会误导关键字 - 抱歉。

标签: sql database oracle postgresql plsql


【解决方案1】:

这是我在 SQL Server 中的操作方式:

SELECT * FROM table WHERE id in
(SELECT top 1 id FROM table WHERE time<selectionDate GROUP BY [group] ORDER BY [time])

【讨论】:

    【解决方案2】:

    解决方案因数据库服务器而异,因为 TOP 查询的语法各不相同。基本上,您正在寻找“top n per group”查询,因此您可以根据需要在 Google 上搜索。

    这是 SQL Server 中的一个解决方案。以下将返回自 1990 年以来每年击出最多本垒打的前 10 名球员。关键是计算每个球员每年的“本垒打排名”。

    select 
      HRRanks.*
    from
    (
        Select 
          b.yearID, b.PlayerID, sum(b.Hr) as TotalHR,
          rank() over (partition by b.yearID order by sum(b.hr) desc) as HR_Rank
        from 
          Batting b
        where 
          b.yearID > 1990
        group by 
          b.yearID, b.playerID
    ) 
      HRRanks
    where
      HRRanks.HR_Rank <= 10
    

    这是 Oracle 中的一个解决方案(每个部门的顶级销售人员)

    SELECT deptno, avg_sal
    FROM( 
          SELECT deptno, AVG(sal) avg_sal
          GROUP BY deptno
          ORDER BY AVG(sal) DESC
        )
    WHERE ROWNUM <= 10;
    

    或者使用解析函数:

    SELECT deptno, avg_sal
    FROM (
           SELECT deptno, avg_sal, RANK() OVER (ORDER BY sal DESC) rank
           FROM
           (
             SELECT deptno, AVG(sal) avg_sal
             FROM emp
             GROUP BY deptno
           )
         )
    WHERE rank <= 10;
    

    或再次相同,但使用 DENSE_RANK() 而不是 RANK()

    【讨论】:

      【解决方案3】:

      以下内容适用于 SQL Server 2005+ 和 Oracle 9i+:

      WITH groups AS (
             SELECT t.group,
                    MAX(t.time) 'maxtime'
               FROM TABLE t
           GROUP BY t.group)
      SELECT t.id,
             t.time,
             t.group
        FROM TABLE t
        JOIN groups g ON g.group = t.group AND g.maxtime = t.time
      

      任何数据库都应该支持:

      SELECT t.id,
             t.time,
             t.group
        FROM TABLE t
        JOIN (SELECT t.group,
                     MAX(t.time) 'maxtime'
                FROM TABLE t
            GROUP BY t.group) g ON g.group = t.group AND g.maxtime = t.time
      

      【讨论】:

      • +1。很像第二个版本,虽然它假设一个组每个“时间”只出现一次。 Thilo 使用 WHERE ... IN *subquery* 为您的第二个查询提供了等效解决方案,但似乎已被删除。
      • +1:我想我会选择你的第二个解决方案。第一次测试似乎很有希望。再次感谢您和所有其他人如此快速和专业地帮助我。谢谢!
      【解决方案4】:
      select * from TABLE where (GROUP, TIME) in (
          select GROUP, max(TIME) from things
              where TIME >= 20090804
              group by GROUP
          )
      

      用 MySQL 测试(但我不得不更改表和列名,因为它们是关键字)。

      【讨论】:

        【解决方案5】:

        选择 * 来自TABB T1

        QUALIFY ROW_NUMBER() OVER (PARTITION BY GROUPP,TIMEE order by id desc)=1

        【讨论】:

          猜你喜欢
          • 2023-03-14
          • 2011-03-13
          • 1970-01-01
          • 2012-11-24
          • 2014-03-10
          • 1970-01-01
          • 2014-12-05
          • 2013-10-10
          • 1970-01-01
          相关资源
          最近更新 更多