2009-08-05 142 views
2

在我的項目中選擇數據時出現性能問題。Tricky SQL SELECT語句

有3列的表:「ID」,「時間」和「組」

  • 的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專家可以幫助我找到解決方案將這些步驟放在一個單一的聲明。

謝謝!

+0

您正在使用什麼數據庫?更高級的功能差別很大。 – Thilo 2009-08-05 05:36:07

+0

'TIME','TABLE'和'GROUP'是保留的SQL關鍵字,因此,我發現它們有點誤導爲SQL對象的名稱,如表和列。 – pilcrow 2009-08-05 05:44:33

+0

首先,感謝迄今爲止的快速和偉大的答案。 我真的忘了一些細節:我正在使用ORACLE,但最好該解決方案還應該可以與Postgresql一起使用。 是的,名稱確實是誤導性的關鍵字 - 對不起。 – 2009-08-05 05:46:58

回答

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 
+0

+1。非常喜歡第二個版本,儘管它假設一個組在每個「時間」只出現一次。 Thilo使用'WHERE ... IN * subquery *'具有與第二個查詢等效的解決方案,但似乎已被刪除。 – pilcrow 2009-08-05 05:48:01

+0

+1:我想我會選擇你的第二個解決方案。首先測試看起來很有希再次感謝你和所有其他人幫助我如此迅速和專業。謝謝! – 2009-08-05 06:10:07

5

這是我會怎麼做在SQL Server:

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

該解決方案將通過數據庫服務器有所不同,因爲對於TOP查詢語法變化。基本上,你正在尋找一個「前n組」查詢,所以你可以谷歌,如果你想。

這是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; 

還是一樣了,但是使用的,而不是RANK DENSE_RANK() ()

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

與MySQL測試(但我不得不改變表和列名,因爲它們是關鍵字)。

0

SELECT * FROM TABB T1

QUALIFY ROW_NUMBER()OVER(PARTITION BY GROUPP,TIMEE ORDER BY ID DESC)= 1