4

可以說我有所謂的「刮」可能設置像一個數據庫表:如何查詢我的數據庫中用戶的排名,但只考慮每個用戶的最新條目?

UserID (int) 
UserName (varchar) 
Wins (int) 
Losses (int) 
ScrapeDate (datetime) 

我想能夠根據他們的勝利/損失比率排名我的用戶。但是,每週我都會爲用戶提供新的數據,並在Scrape表中創建另一個條目。

如何查詢根據勝/損失排序的用戶列表,但僅考慮最近的條目(ScrapeDate)?

另外,你認爲重要的是人們會擊中該網站,並可能在刮完成?

例如,我可以有:

1 - Bob - Wins: 320 - Losses: 110 - ScrapeDate: 7/8/09 
1 - Bob - Wins: 360 - Losses: 122 - ScrapeDate: 7/17/09 
2 - Frank - Wins: 115 - Losses: 20 - ScrapeDate: 7/8/09 

其中,這表示只更新鮑勃至今一刮,並在更新弗蘭克的過程,但還沒有被插入。你將如何處理這種情況呢?

所以,我的問題是:

  1. 你會如何處理查詢僅在每個用戶的最近湊來確定排名
  2. 你認爲的事實,該數據庫可以在狀態更新(尤其是如果一個刮可能需要1天才能完成),並不是所有的用戶已經完全更新,但重要嗎?如果是這樣,你會如何處理?

謝謝你,感謝你給我在我相關的問題您的回答:

When scraping a lot of stats from a webpage, how often should I insert the collected results in my DB?

+0

哪個版本的SQL Server? – 2009-09-09 06:49:43

+0

將使用MSSQL – Fatal510 2009-09-09 06:52:12

+0

但是哪個版本,SQL 7,SQL 2000,SQL 2005,SQL 2008,SQL 2008 R2? – mrdenny 2009-09-09 07:34:58

回答

3

這就是我所說的「每組最大的」問題。它每週在StackOverflow上出現好幾次。

我解決這類問題使用外連接技術:

SELECT s1.*, s1.wins/s1.losses AS win_loss_ratio 
FROM Scrape s1 
LEFT OUTER JOIN Scrape s2 
    ON (s1.username = s2.username AND s1.ScrapeDate < s2.ScrapeDate) 
WHERE s2.username IS NULL 
ORDER BY win_loss_ratio DESC; 

這將返回只有一排爲每個用戶名 - 與ScrapeDate列的最大價值的行。這就是外部連接的目的,以嘗試來匹配s1與其他行s2具有相同的用戶名和更大的日期。如果不存在這樣的行,則外部聯接將爲s2的所有列返回NULL,然後我們知道s1對應於具有給定用戶名的最長日期的行。

當你有一個部分完成的刮進程序時,這也應該工作。

這種技術不一定像其他答案所提供的CTE和RANKING解決方案一樣快。你應該嘗試兩種方法,看看哪種方法對你更好。我更喜歡我的解決方案的原因是它適用於任何SQL語言。

0

回答你的問題的第一部分取決於該版本的SQL服務器的你使用 - SQL 2005+提供了ranking functions,這使得這種查詢比SQL 2000和之前更簡單一些。如果您將指出您正在使用哪個平臺,我會更詳細地更新它。

我懷疑處理第2部分最明顯的方法是顯示最新的完整刮擦練習的統計數據,否則你不顯示時間一致的排名(儘管如果你的數據收集練習需要24小時,一定的緯度已經)。

爲了簡化這一點,您可以創建一個表來保存關於每個刮擦操作的元數據,爲每個刮擦操作提供一個id,開始日期和完成日期(至少),並顯示與最新完整刮擦相關的記錄。爲了使這更容易,您可以從數據收集表中刪除「刮日期」,並將其替換爲將每個數據行鏈接到刮表中的行的外鍵。

編輯

下面的代碼演示瞭如何通過他們最新的得分排名的用戶,不管他們是否有時間一致:

create table #scrape 
(userName varchar(20) 
,wins int 
,losses int 
,scrapeDate datetime 
) 

INSERT #scrape 
     select 'Alice',100,200,'20090101' 
union select 'Alice',120,210,'20090201' 
union select 'Bob' ,200,200,'20090101' 
union select 'Clara',300,100,'20090101' 
union select 'Clara',300,210,'20090201' 
union select 'Dave' ,100,10 ,'20090101' 


;with latestScrapeCTE 
AS 
(
     SELECT * 
       ,ROW_NUMBER() OVER (PARTITION BY userName 
            ORDER BY scrapeDate desc 
           ) AS rn 
       ,wins + losses AS totalPlayed 
       ,wins - losses as winDiff 
     from #scrape 
) 
SELECT userName 
     ,wins 
     ,losses 
     ,scrapeDate 
     ,winDiff 
     ,totalPlayed 
     ,RANK() OVER (ORDER BY winDiff desc 
           ,totalPlayed desc 
        ) as rankPos 
FROM latestScrapeCTE 
WHERE rn = 1 
ORDER BY rankPos 

EDIT 2

一個例證使用元數據表來選擇最新的完整刮:

create table #scrape_run 
(runID int identity 
,startDate datetime 
,completedDate datetime 
) 

create table #scrape 
(userName varchar(20) 
,wins int 
,losses int 
,scrapeRunID int 
) 


INSERT #scrape_run 
select '20090101', '20090102' 
union select '20090201', null --null completion date indicates that the scrape is not complete 

INSERT #scrape 
     select 'Alice',100,200,1 
union select 'Alice',120,210,2 
union select 'Bob' ,200,200,1 
union select 'Clara',300,100,1 
union select 'Clara',300,210,2 
union select 'Dave' ,100,10 ,1 


;with latestScrapeCTE 
AS 
(
     SELECT TOP 1 runID 
        ,startDate 
     FROM #scrape_run 
     WHERE completedDate IS NOT NULL 
) 
SELECT userName 
     ,wins 
     ,losses 
     ,startDate  AS scrapeDate 
     ,wins - losses AS winDiff 
     ,wins + losses AS totalPlayed 
     ,RANK() OVER (ORDER BY (wins - losses) desc 
           ,(wins + losses) desc 
        ) as rankPos 
FROM #scrape 
JOIN latestScrapeCTE 
ON runID = scrapeRunID 
ORDER BY rankPos 
+0

假設SQL 2005 – Fatal510 2009-09-09 08:51:44

0

試着這麼做:

  1. 選擇用戶名併爲每個用戶最後一個條目的最大日期。
  2. 根據上述查詢結果選擇記錄進行排序。

這應該工作,但取決於您的數據庫大小。

DECLARE 
    @last_entries TABLE(id int, dte datetime) 

-- insert date (dte) of last entry for each user (id) 
INSERT INTO 
    @last_entries (id, dte) 
SELECT 
    UserID, 
    MAX(ScrapeDate) 
FROM 
    Scrape WITH (NOLOCK) 
GROUP BY 
    UserID 

-- select ranking 
SELECT 
    -- optionally you can use RANK OVER() function to get rank value 
    UserName, 
    Wins, 
    Losses 
FROM 
    @last_entries 
    JOIN 
     Scraps WITH (NOLOCK) 
    ON 
     UserID = id 
     AND ScrapeDate = dte 
ORDER BY 
    Winds, 
    Losses 

我不測試這段代碼,所以無法在第一次運行時編譯。

相關問題