棘手的SQL SELECT语句

时间:2009-08-05 05:23:12

标签: sql database oracle postgresql plsql

在项目中选择数据时出现性能问题。

有一个包含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)中找到的每个结果的第一行
  4. 显然这不是最佳的。

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

    谢谢!

5 个答案:

答案 0 :(得分:10)

以下内容适用于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 :(得分:5)

以下是我将如何在SQL Server中执行此操作:

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

答案 2 :(得分:1)

解决方案因数据库服务器而异,因为TOP查询的语法各不相同。基本上您正在寻找一个“每个组的前n个”查询,因此如果您愿意,可以使用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 :(得分:0)

select * from TABLE where (GROUP, TIME) in (
    select GROUP, max(TIME) from things
        where TIME >= 20090804
        group by GROUP
    )

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

答案 4 :(得分:0)

SELECT * 来自TABB T1

QUALIFY ROW_NUMBER()OVER(按GROUPP划分,TIMEE按ID desc排序)= 1