2012-01-13 26 views
2

我有一個表格,用於跟蹤發生在任意時間點的機器維護情況。這裏有一個簡單的表結構:選擇表格中每一行的前一個日期

Maintenance Table 
---------------------------------------- 
ID   - integer 
DateCompleted - date 
MachineName - varchar 

和這裏的一些示例表數據:

ID DateCompleted MachineName 
---------------------------------------- 
1  1/6/2011  'Machine 1' 
2  1/13/2011 'Machine 2' 
3  1/14/2011 'Machine 1' 
4  2/2/2011  'Machine 3' 
5  2/26/2011 'Machine 1' 
6  3/9/2011  'Machine 2' 
7  4/20/2011 'Machine 3' 

我想要做的就是創建一個查詢,將返回從以前的維修任務的日期爲每個任務。所以結果集是這樣的:

ID MachineName CurDate  PrevDate 
---------------------------------------- 
1 'Machine 1' 1/6/2011 NULL 
2 'Machine 2' 1/13/2011 NULL 
3 'Machine 1' 1/14/2011 1/6/2011 
4 'Machine 3' 2/2/2011 NULL 
5 'Machine 1' 2/26/2011 1/14/2011 
6 'Machine 2' 3/9/2011 1/13/2011 
7 'Machine 3' 4/20/2011 2/2/2011 

什麼是最好的方式來寫這樣的查詢?我唯一的想法到目前爲止是這樣的:

SELECT ID, MachineName, DateCompleted AS CurDate, 
    (
    SELECT TOP 1 DateCompleted FROM Maintenance m2 
    WHERE m1.MachineName = m2.MachineName 
     AND m1.DateCompleted > m2.DateCompleted 
    ORDER BY DateCompleted DESC 
) AS PrevDate 

FROM Maintenance m1 

ORDER BY ID 

任何想法,建議或修正將是非常歡迎的。

+0

您使用的是什麼RDBMS? – Lamak 2012-01-13 19:39:49

+0

微軟,但我歡迎任何解決方案。 – Sparafusile 2012-01-13 19:44:34

+2

「Microsoft」不是RDBMS。 「SQL Server」是一個dbms。我爲你加了標籤。 – 2012-01-13 20:00:55

回答

1

至於你說的 「,但我歡迎任何解決方案。」

這與ANSI SQL的溶液:

SELECT ID, 
     DateCompleted, 
     MachineName, 
     lag(DateCompleted) over (partition by MachineName order by DateCompleted) as PrevDate 
FROM Maintenance 
ORDER BY id; 

工程PostgreSQL中,甲骨文,DB2和Teradata。

SQL Server尚不支持lag()函數,但即將推出的「Denali」版本(2012)將擁有它。

+0

這是一個整潔的功能。太糟糕了,這不是每個人都支持的。 – Sparafusile 2012-01-14 02:32:48

+0

@Sparafusile:窗口函數是一個重要的功能(如遞歸查詢),現在我認爲DBMS不支持它們不再是「最先進的」(火鳥3.0也會有它們) – 2012-01-14 11:56:04

+0

正確,我誤解了你的陳述。我以爲你在微軟說「Windows」。更多的閱讀後,我意識到我的錯誤。這是我正在尋找的解決方案類型,非常感謝。 – Sparafusile 2012-01-14 19:41:51

1

如何:

SELECT 
    m.ID, m.MachineName, m.DateCompleted AS CurDate, MAX(m_past.DateCompleted) AS PrevDate 

FROM Maintenance m 

    LEFT JOIN Maintenance m_past 
    ON m.MachineName = m_past.MachineName 

WHERE m_past.DateCompleted < m.DateCompleted 

GROUP BY m.ID 
+0

您的'GROUP BY'將阻止您選擇'm.MachineName,m.DateCompleted AS CurDate'。我建議從select中刪除'm.ID',並用'm.MachineName,m.DateCompleted'分組。 – jzila 2012-01-13 19:49:13

+0

這比我原來的解決方案更好,性能更好嗎?如果是這樣,爲什麼? – Sparafusile 2012-01-13 19:49:56

+0

@jzilla:你可能是對的,但我沒有看到它。另外,如果這是真的,我不能只使用'GROUP BY m.ID,m.MachineName,m.DateCompleted'? @Sparafusile:我認爲你的方法將執行內部查詢(每行一個),而我的不會。雖然'JOIN'可能會讓它檢索更多的數據,你需要測試。別忘了在'MachineName'上放一個索引! – mbillard 2012-01-13 20:10:34

1

試試這個:

SELECT A.Id, A.MachineName, A.DateCompleted [CurDate], B.DateCompleted PrevDate 
FROM Maintenance A 
OUTER APPLY (SELECT TOP 1 * 
      FROM Maintenance 
      WHERE MachineName = A.MachineName AND DateCompleted < A.DateCompleted 
      ORDER BY DateCompleted DESC) B 
+0

Downvoter關心評論? – Lamak 2012-01-13 19:53:38

1

無論是前N個工作取決於您的DBMS。 MAX()將跨平臺工作。索引DateCompleted和MachineName;它們都用在WHERE子句中。

select m1.id, m1.machinename, m1.datecompleted as curdate, 
    (select max(datecompleted) 
    from maintenance 
    where machinename = m1.machinename 
     and datecompleted < m1.datecompleted) as prevdate 
from maintenance m1 
order by machinename, curdate 

如果你的DBMS支持窗口功能,您可以使用

select m1.id, m1.machinename, m1.datecompleted as curdate, 
     max(datecompleted) over (partition by machinename 
           order by m1.datecompleted 
           rows between unbounded preceding 
             and 1 preceding) as prevdate 
from maintenance m1 

我不會試圖去猜測這會更快。我會加載一張你想要的具有儘可能多樣本數據的表格,然後測試它們。然後我用10倍的數據重新加載並再次測試。

在測試過程中,您想了解如何generate and read an execution plan

+0

謝謝。儘管如此,這與我的解決方案基本相同。我希望能有一些我以前還沒有的深刻的東西。 – Sparafusile 2012-01-13 19:51:51

+0

你知道窗口函數的好處。生成數據總是比存儲數據更昂貴。 – 2012-01-13 20:07:01

0

您的查詢對我來說似乎很合理,而且很容易理解。忽略最終排序的可能成本,我認爲假設存在適當的索引,複雜度本質上是O(n log n)。對於表中的每個條目,查詢引擎都必須定位先前的日期條目,該條目應該是O(log n)且具有正確的索引。

單向也許以代碼複雜度爲代價提高性能可能是編寫存儲過程以產生結果。我認爲無序的結果可以在O(n)中產生。該過程可以通過MachineName,然後由DateCompleted按順序遍歷表上的兩個遊標。它可以在O(n)中構建結果集,因爲它跨過兩個遊標。但是,結果將需要在ID上排序,這將是O(n log n)。所以我認爲理論上的複雜性與查詢相同,但是這個過程可能會有更少的開銷並且運行速度會更快。但我絕對不會推薦這個解決方案,因爲它會變得醜陋難以維護。

+0

這實際上很簡單,可以通過單個表掃描來完成支持窗口功能的任何DBMS。看到我的答案。 – 2012-01-13 21:45:37

1

SOLUTION:

declare @tmp table (Id int, DateCompleted datetime, MachineName varchar(100)) 
insert into @tmp 
select 1,'1/6/2011','Machine 1' 
union select 2,'1/13/2011', 'Machine 2' 
union select 3,'1/14/2011', 'Machine 1' 
union select 4,'2/2/2011',  'Machine 3' 
union select 5,'2/26/2011', 'Machine 1' 
union select 6,'3/9/2011',  'Machine 2' 
union select 7,'4/20/2011', 'Machine 3' 



select t.Id, t.DateCompleted, t.MachineName, max(t2.DateCompleted) PrevDate 
from @tmp t 
left join @tmp t2 
    on t.MachineName = t2.MachineName 
    and t.DateCompleted > t2.DateCompleted 
group by t.Id, t.DateCompleted, t.MachineName 
1

從SQL Server 2012開始,您可以使用窗口集合來編寫您需要的查詢。只需使用以下代碼:

select 
    ID, 
    MachineName, 
    DateCompleted AS CurDate, 
    min(DateCompleted) 
     over (partition by MachineName order by DateCompleted 
      rows between 1 preceding and 1 preceding) as PrevDate 
from Maintenance 
order by Id 
相關問題