11

我有以下表格:

tblPerson:在查詢中檢索最近的記錄

PersonID | Name 
--------------------- 
    1  | John Smith 
    2  | Jane Doe 
    3  | David Hoshi 

tblLocation:

LocationID | Timestamp | PersonID | X | Y | Z | More Columns... 
--------------------------------------------------------------- 
    40  | Jan. 1st |  3 | 0 | 0 | 0 | More Info... 
    41  | Jan. 2nd |  1 | 1 | 1 | 0 | More Info... 
    42  | Jan. 2nd |  3 | 2 | 2 | 2 | More Info... 
    43  | Jan. 3rd |  3 | 4 | 4 | 4 | More Info... 
    44  | Jan. 5th |  2 | 0 | 0 | 0 | More Info... 

我可以產生一個SQL查詢獲取位置記錄每個人都這樣:

SELECT LocationID, Timestamp, Name, X, Y, Z 
FROM tblLocation 
JOIN tblPerson 
ON tblLocation.PersonID = tblPerson.PersonID; 

產生以下內容:

LocationID | Timestamp | Name  | X | Y | Z | 
-------------------------------------------------- 
    40  | Jan. 1st | David Hoshi | 0 | 0 | 0 | 
    41  | Jan. 2nd | John Smith | 1 | 1 | 0 | 
    42  | Jan. 2nd | David Hoshi | 2 | 2 | 2 | 
    43  | Jan. 3rd | David Hoshi | 4 | 4 | 4 | 
    44  | Jan. 5th | Jane Doe | 0 | 0 | 0 | 

我的問題是我們只關心最近的位置記錄。因此,我們只對以下行感興趣:LocationID 41,43和44.

問題是:我們如何查詢這些表格以提供有關每個人的最新數據基礎?需要發生什麼特別的分組來產生期望的結果?

+0

你可以發佈最終解決方案plzz – 2011-12-03 06:27:01

回答

19

MySQL沒有排名/分析/窗口功能。

SELECT tl.locationid, tl.timestamp, tp.name, X, Y, Z 
    FROM tblPerson tp 
    JOIN tblLocation tl ON tl.personid = tp.personid 
    JOIN (SELECT t.personid, 
       MAX(t.timestamp) AS max_date 
      FROM tblLocation t 
     GROUP BY t.personid) x ON x.personid = tl.personid 
          AND x.max_date = tl.timestamp 

的SQL Server 2005+和Oracle 9i +支持分析,因此您可以使用:

SELECT x.locationid, x.timestamp, x.name, x.X, x.Y, x.Z 
    FROM (SELECT tl.locationid, tl.timestamp, tp.name, X, Y, Z, 
       ROW_NUMBER() OVER (PARTITION BY tp.name ORDER BY tl.timestamp DESC) AS rank 
      FROM tblPerson tp 
      JOIN tblLocation tl ON tl.personid = tp.personid) x 
WHERE x.rank = 1 

使用變量獲得相同MySQL的ROW_NUMBER功能:

SELECT x.locationid, x.timestamp, x.name, x.X, x.Y, x.Z 
    FROM (SELECT tl.locationid, tl.timestamp, tp.name, X, Y, Z, 
       CASE 
       WHEN @name != t.name THEN 
        @rownum := 1 
       ELSE @rownum := @rownum + 1 
       END AS rank, 
       @name := tp.name 
      FROM tblLocation tl 
      JOIN tblPerson tp ON tp.personid = tl.personid 
      JOIN (SELECT @rownum := NULL, @name := '') r 
     ORDER BY tp.name, tl.timestamp DESC) x 
WHERE x.rank = 1 
+0

感謝OMG。我拿走了SQL Server 2005並對其進行了一些修改,以產生所需的結果。 – 2010-07-06 13:47:27

3

這是幾乎每天都會出現在Stack Overflow上的經典「最大每組」問題。有很多方法可以解決它,你可以通過searching Stack Overflow找到示例解決方案。這裏有一種方法可以在MySQL中執行:

SELECT 
    location.LocationId, 
    location.Timestamp, 
    person.Name, 
    location.X, 
    location.Y, 
    location.Z 
FROM (
    SELECT 
     LocationID, 
     @rn := CASE WHEN @prev_PersonID = PersonID 
        THEN @rn + 1 
        ELSE 1 
       END AS rn, 
     @prev_PersonID := PersonID 
    FROM (SELECT @prev_PersonID := NULL) vars, tblLocation 
    ORDER BY PersonID, Timestamp DESC 
) T1 
JOIN tblLocation location ON location.LocationID = T1.LocationId 
JOIN tblPerson person ON person.PersonID = location.PersonID 
WHERE rn = 1 
3

正如@馬克·拜爾斯提到,這個問題在Stack Overflow上經常出現。

這裏的解決方案,我最常建議,鑑於你的表格:

SELECT p.*, l1.* 
FROM tblPerson p 
JOIN tblLocation l1 ON p.PersonID = l1.PersonID 
LEFT OUTER JOIN tblLocation l2 ON p.PersonID = l2.PersonID AND 
    (l1.timestamp < l2.timestamp OR l1.timestamp = l2.timestamp AND l1.LocationId < l2.LocationId) 
WHERE l2.LocationID IS NULL; 

要查看其他的例子,按照標籤greatest-n-per-group,我加入到你的問題。

+0

感謝您爲我添加標籤Bill! – 2010-07-06 13:45:31