2011-06-17 15 views
5

我的僱主有一個批處理計算羣集來處理用戶提交的作業。每個批處理作業包括三個步驟:做一個pivot-table-ish在SQL中加入聯繫我

  1. 作業啓動
  2. 工作完成
  3. 結果報告給用戶

批處理作業管理軟件日誌時,每個步驟發生,日誌文件組成與提交作業的員工的ID代碼相關的元組,發生了什麼步驟以及發生何時的時間戳。在CSV中,它看起來像:

ID step timestamp 
-- ------ --------- 
A start 3533 
B start 3538 
B finish 3549 
C start 3551 
A finish 3557 
B report 3559 
C finish 3602 
A report 3603 
B start 3611 
C report 3623 
B finish 3643 
B report 3657 

等等。

該數據集的另外一個特點是員工之間存在競爭,但員工之間沒有競爭;即每個員工在下一份工作開始前都必須等到他們目前的工作已經報告。因此,當我按日期排序並將結果限制爲單個員工時,記錄始終按照「開始」,「完成」,「報告」的順序顯示。

我想創建一個數據透視表,將每個作業分組到一行中。因此,上述數據變爲:

employee-ID started finished reported 
----------- ------- -------- -------- 
A   3533  3557  3603 
B   3538  3549  3559 
B   3611  3643  3657 
C   3551  3602  3623 

所以,到SQL:

SELECT 
    log.ID AS employee-ID, 
    start.timestamp AS started, 
    finish.timestamp AS finished, 
    report.timestamp AS reported 
FROM 
    log 

    LEFT OUTER JOIN log start ON 
    log.ID = start.ID 
     AND start.step = 'start' 

    LEFT OUTER JOIN log finish ON 
    log.ID = finish.ID 
     AND finish.step = 'finish' 
     AND start.timestamp < finish.timestamp 

    LEFT OUTER JOIN log report ON 
    log.ID = report.ID 
     AND report.step = 'report' 
     AND finish.timestamp < report.timestamp 

ORDER BY employee-ID,started,finished,reported; 

我需要LEFT OUTER JOIN,因爲我還需要確定啓動的,但並沒有完成,或者工作報道。

這工作得很好。它確實給我我需要的行。但是它給了我很多虛假的行,因爲除了當前的工作之外,JOIN匹配finishreport條目以用於同一僱員的未來工作。因此,報告找出來,如:

employee-ID started finished reported 
----------- ------- -------- -------- 
A   3533  3557  3603 
B   3538  3549  3559 
B   3538  3549  3657 <-- spurious 
B   3538  3643  3657 <-- spurious 
B   3611  3643  3657 
C   3551  3602  3623 

可以很容易地識別虛假行:每個工作得到只啓動一次,所以給出的排序,正確的行是第一行有一個獨特的「開始」值。我現在正在應用程序級別上通過跳過虛假行來解決虛假行問題,但看起來好像不夠優雅。而且代價高昂:其中一些員工提交了數十份工作,因此目前我的查詢結果大約有15%是合法的,85%是虛假的。這是浪費時間跳過虛假條目的很多時間。如果每項工作都有唯一的ID,但我只是沒有這些數據,那就太好了。

我需要以某種方式限制JOIN,以便每個「已啓動」條目只選取一個「已完成」和「已報告」條目:具有大於前一步驟的時間戳的最小時間戳的單個條目。我試圖通過使用子查詢作爲我正在JOINing的表,但我無法弄清楚如何在沒有相關子查詢的情況下執行此操作。我也嘗試通過使用「GROUP BY employee-ID,開始」來做到這一點,但這並不一定會選擇「正確」的行。 「GROUP BY」報道的大部分行都是錯誤的行。

因此,SQL大師,是否可以報告我需要的行?如果是這樣,怎麼樣?我現在正在使用sqlite3,但如果需要可以將數據庫傳輸到MySQL。

回答

2

問題是你是如何加入到finishreport

你不想start.timestamp < finish.timestamp你真的想start.timestamp < MIN(finish.timestamp)

當然不行,所以你必須這樣做加入後。

例如

SELECT 
    log.ID AS employee_ID, 
    start.timestamp AS started, 
    MIN(finish.timestamp) AS finished, 
    MIN(report.timestamp) AS reported 
FROM 
    log 


LEFT OUTER JOIN log start ON 
log.ID = start.ID 
    AND start.step = 'start' 

LEFT OUTER JOIN log finish ON 
log.ID = finish.ID 
    AND finish.step = 'finish' 
    AND start.timestamp < finish.timestamp 

LEFT OUTER JOIN log report ON 
log.ID = report.ID 
    AND report.step = 'report' 
    AND finish.timestamp < report.timestamp 

GROUP BY log.ID, 
    start.timestamp 
ORDER BY 
    employee_ID,started,finished,reported 

而且你可以開始可能轉換爲內部連接,因爲它不會使一大堆的意義有一個結束沒有開始

+0

完美的作品!它使查詢速度降低了不到5%,並且現在應用程序運行得非常快,因爲它不必自己進行過濾。非常感謝。 – STH