2011-09-05 66 views
1

有時我想加入一條記錄,我可以輕鬆識別爲「按此排序並選擇第一條記錄」。我想知道這裏的最佳做法。SQL以自定義排序順序加入第一條記錄

例如在我的數據模型中我有服務和實現。服務可能有多個實現。實現有版本號;可以安裝零個或多個實現,並且還可以啓用其中的一個。現在我想使用以下規則爲每個服務選擇「當前實現」:

  • 如果沒有安裝任何內容,那麼當前的實現是最近的一個。
  • 如果安裝了多個安裝,那麼當前是啓用的或者如果沒有啓用,則再次是最新的。

一些例子:

Service Version Installed Enabled 
======= ======= ========= ======= 
A  1  False  False 
A  2  False  False <- current for service A because most recent 

B  1  True  False <- current for service B because installed 
B  2  False  False 

C  1  True  False 
C  2  True  False <- current for service C because most recent 
C  3  False  False  among installed 

D  1  True  True <- current for service D because enabled 
D  2  True  False 

我想,如果我有點被這些田裏挖第一個記錄,它會做的伎倆。即我這樣做:

SELECT s.service, <other service fields>, i.version, <other impl. fields> 
    FROM service s, implementation i 
WHERE i.rowid == (
     SELECT rowid 
     FROM implementation o 
     WHERE o.service == s.service 
    ORDER BY installed DESC, enabled DESC, version ASC) 

更新。我猜這是一個比較特殊的SQLite。首先,rowid是SQLite使用的內部記錄標識。其次,在SQLite中,此上下文中的SELECT表達式返回單個值。我想這可能被改寫成一個更通用的SQL是這樣的:

... 
    FROM service s, implementation i 
WHERE i.service == s.service 
    AND i.version == (
     SELECT version 
     FROM ... 
    ORDER BY ... 
     LIMIT 1) 

它在這裏工作,但有時沒有rowid或其他單一領域,可以作爲一個標識符。有沒有其他方法可以獲得相同的結果?也許更通用一些? (對我來說更具體的東西也沒關係,只要它是SQLite的特定:)

+0

是否有某些原因您不使用INNER JOIN? –

+0

你確定這可以嗎?因爲子查詢可能會返回多於一行,並且您在rowid上使用了相等運算符。 – NullUserException

+0

我可以,但我不明白它會在這裏有什麼不同。一個內部聯接會給我所有匹配的實現,這可以作爲一個起點,但是我想選擇一個以任何排序順序排在第一位的實現:) –

回答

3

您可以使用NOT EXISTS條件排除除期望匹配以外的所有條件。加入與[service]列匹配的[service]和[implementation]的行,只要沒有[[implementation]]的另一行也與[service]列相匹配,但是是首選的,因爲它會在所需的順序。這是主意。

SELECT s.service, <other service fields>, i.version, <other impl. fields> 
FROM service s JOIN implementation i 
ON i.service = s.service 
AND NOT EXISTS (-- where there's no "better" row from i to use 
SELECT * FROM implementation AS i2 
    WHERE i2.service = i.service 
    AND (
    i2.installed > i.installed 
    OR (i2.installed = i.installed AND i2.enabled > i.enabled) 
    OR (i2.installed = i.installed AND i2.enabled = i.enabled AND i2.version < i.version) 
    ) 
) 

使用Microsoft SQL Server或支持CROSS APPLY操作另一個SQL方言,這是非常非常簡單:

SELECT s.service, <other service fields>, i.version, <other impl. fields> 
FROM service s 
CROSS APPLY (
    SELECT TOP 1 * FROM implementation 
    WHERE implementation.service = s.service 
    ORDER BY installed DESC, enabled DESC, version ASC 
) AS i 

(無論是解決方案已經與樣本數據進行測試,CREATE TABLE和INSERT語句未發佈。)

Steve Kass

+0

謝謝,史蒂夫;這看起來非常有前途:) –

+0

+1,似乎絕對比我的解決方案中的一羣GROUP BY更好。 –

0

我想不出什麼比這更好的:

SELECT 
    i.Service, 
    i.Installed, 
    i.Enabled, 
    MAX(i.Version) AS Version 
FROM implementation i 
    INNER JOIN (
    SELECT 
     i.Service, 
     i.Installed, 
     MAX(i.Enabled) AS Enabled 
    FROM implementation i 
     INNER JOIN (
     SELECT 
      Service, 
      MAX(Installed) AS Installed 
     FROM implementation 
     GROUP BY 
      Service 
    ) m ON i.Service = m.Service 
    GROUP BY 
     i.Service, 
     i.Installed 
) m ON i.Service = m.Service AND i.Installed = m.Installed 
GROUP BY 
    i.Service, 
    i.Installed 
    i.Enabled 

基本上這個腳本發現的Installed最大值對於每個Service,然後將獲得的列表加回到原始表格(以過濾所找到的值)並找到最大值Enabled,然後再次將獲取的表格加回到implementation,最終檢索過濾行的最大值Version

+0

我想我明白了,但我必須承認這實際上很難寫出如此錯綜複雜的聲明:) –