4

假设我有一个名为“Scrape”的数据库表,可能设置如下:

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  

其中,这表示到目前为止只更新了 Bob,并且正在更新 Frank 但尚未插入的抓取。你也将如何处理这种情况?

所以,我的问题是:

  1. 您将如何处理仅查询每个用户的最新抓取以确定排名
  2. 您是否认为数据库可能处于更新状态(特别是如果一次抓取可能需要 1 天才能完成)并且并非所有用户都已完全更新这一事实很重要?如果是这样,您将如何处理?

谢谢你,也谢谢你对我的相关问题给我的答复:

从网页中抓取大量统计信息时,我应该多久将收集到的结果插入我的数据库中?

4

3 回答 3

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如果没有这样的行,则外连接为 的所有列返回 NULL s2,然后我们知道s1对应于给定用户名的日期最大的行。

当您正在进行部分完成的刮擦时,这也应该起作用。

这种技术不一定像其他答案给出的 CTE 和 RANKING 解决方案那样快速。您应该尝试两者,看看哪种更适合您。我更喜欢我的解决方案的原因是它适用于任何类型的 SQL。

于 2009-09-18T16:50:42.473 回答
0

您的问题第一部分的答案取决于您使用的 SQL 服务器的版本 - SQL 2005+ 提供了排名功能,这使得这种查询比 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

编辑 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
于 2009-09-09T06:59:44.123 回答
0

尝试类似:

  1. 为每个用户选择用户 ID 和最后一次进入的最大日期。
  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

我没有测试这段代码,所以它在第一次运行时无法编译。

于 2009-09-09T07:26:17.333 回答