2012-12-03 98 views
4

我有一個表使用兩個標識列,我們稱它們爲id和userid。 ID在每個記錄中都是唯一的,用戶ID對用戶來說是唯一的,但在許多記錄中。Inner自己加入表

我需要做的是通過用戶名爲用戶獲取記錄,然後將該記錄加入到我們爲用戶創建的第一條記錄中。查詢的邏輯如下:

SELECT v1.id, MIN(v2.id) AS entryid, v1.userid 
FROM views v1 
INNER JOIN views v2 
    ON v1.userid = v2.userid 

我希望我沒有加入表到處理該MIN()塊代碼的子查詢的,這似乎是相當緩慢。

+0

你對userid列有索引嗎? –

+0

或者UserId和Id上的複合索引?是該行的PK嗎? –

+0

因爲你應該做一個子查詢,它不應該那麼慢。如果您可以更改數據結構,則可以始終爲用戶的第一條記錄添加一列,並在代碼中保留該列,或者如果這將會太慢,則保留其他表格。 –

回答

12

我想(並不是完全清楚)你想爲每個用戶找到最少有id的表的行,所以每個用戶一行。

在這種情況下,您的使用子查詢(派生表),並將其加入到表:

SELECT v.* 
FROM views AS v 
    JOIN 
    (SELECT userid, MIN(id) AS entryid 
     FROM views 
     GROUP BY userid 
    ) AS vm 
    ON vm.userid = v.userid 
    AND vm.entryid = v.id ; 

以上也可以使用Common Table Expression (CTE)寫的,如果你喜歡他們:

; WITH vm AS 
    (SELECT userid, MIN(id) AS entryid 
     FROM views 
     GROUP BY userid 
    ) 
    SELECT v.* 
    FROM views AS v 
    JOIN vm 
     ON vm.userid = v.userid 
     AND vm.entryid = v.id ; 

這兩個對於(userid, id)上的索引都是非常有效的。

隨着SQL-Server,您可以使用ROW_NUMBER()窗函數寫:

; WITH viewsRN AS 
    (SELECT * 
      , ROW_NUMBER() OVER (PARTITION BY userid ORDER BY id) AS rn 
     FROM views 
    ) 
    SELECT *      --- skipping the "rn" column 
    FROM viewsRN 
    WHERE rn = 1 ; 
+0

謝謝。 CTE是一個巨大的幫助! –

+0

檢查最後一個查詢(我有一個錯誤,它現在已經修復)。窗口功能非常有幫助。 –

1

那麼,要使用MIN函數以及非聚合列,您必須將語句分組。這可能與你的查詢...(編輯基於其他信息)

SELECT MIN(v2.id) AS entryid, v1.id, v1.userid 
FROM views v1 
INNER JOIN views v2 
    ON v1.userid = v2.userid  
GROUP BY v1.id, v1.userid 

...但如果這只是一個簡單的例子,你希望這個查詢快速拉更多的數據,它成爲不可行的解決方案。

你似乎想要的是該視圖中所有用戶數據的列表,每行的鏈接都會引導回同一用戶存在的「第一個」記錄。上面的查詢會得到你想要的東西,但也有更容易的方法來確定每個用戶的第一個記錄:

SELECT v1.id, v1.userid 
FROM views v1 
ORDER BY v1.userid, v1.id 

爲每個唯一用戶的第一個記錄是你的「切入點」。我想我明白你爲什麼要按照你指定的方式來做,而且我給出的第一個查詢將是合理的,但是你必須考慮是否不必使用order by子句來得到正確的答案是值得的。

+0

+1同意......但我的目前看起來他只是需要SELECT MAX(id),MIN(id),userId FROM views GROUP BY userId ...但沒有關於查詢/預期結果的更多信息... IDK。我懷疑他需要你提供的東西,因爲需要更多的數據。 – MikeSmithDev

+0

我實際上需要結果對象中的多行,但這給了我想要的結果。這似乎不起作用的一個實例是什麼時候查詢是針對「入口視圖」,這意味着v1.id和v2.id是相同的。現在查詢的結果中沒有記錄。我假設我可以做一個正確的加入,並且min(v2.id)是null在兩個列中都使用v1.id? –

+0

@MikeSmithDev - 我想我現在明白了;他想要一個查詢,這個查詢會給他所有與用戶相關的行,並且還想知道該用戶的第一個這樣的記錄的ID,這是特別重要的。就我個人而言,我認爲用ORDER BY v1.id'子句可以更好地實現這個目的(然後第一個結果就是你的「入口」),但是如果他正在用反向引用編寫對象映射語句,這可能會簡化一些事情。 – KeithS

-2

編輯-1:在評論中指出,該解決方案還採用了子查詢。但是,它不使用聚合函數,這取決於數據庫可能會對性能產生巨大影響。


可實現無子查詢(見下文)。 顯然,views.userid上的指數對於演出來說具有驚人的價值。

SELECT  v1.* 
FROM  views v1 
WHERE  v1.id = (
     SELECT TOP 1 v2.id 
     FROM views v2 
     WHERE v2.userid = v1.userid 
     ORDER BY v2.id ASC 
    ) 
+0

這是沒有子查詢? –

+0

...您正在使用子查詢。 – KeithS

+1

夠公平的,夥計們。它確實使用子查詢,而不是使用聚合的查詢(我不會更正文本,以便您的評論保持有效)。然而,與'MAX/MIN'連接相比,該解決方案的規模要好得多,因爲可以使用索引。 – van