如何将连接的返回限制为每个表只有一行?

时间:2010-02-25 22:30:11

标签: sql sql-server-2005 tsql join

今天在工作中,我们讨论了哪个是进行这样查询的最佳方式:

例如,假设有一个用户表:

tblUsers
ID      = Autoint
Name    = String

和登录表:

tblLogin
ID         = AUtoint
UserID    = Int
IP        = String
Browser   = String
OS        = String
timestamp = DateTime

列出所有用户以及他们上次登录时(如果有的话)最有效的方法是什么,并提供如下输出:

user       | ip     | timestamp | browser | os   |
-------------------------------------------------
Some User  |1.1.1.1 | 12/12/12  | userBA  | win  |
Other User |1.1.1.1 | 12/12/12  | userBA  | win  |
And Other  |null    | null      | null    | null |
Other Yet  |1.1.1.1 | 12/12/12  | userBA  | win  |

请记住,我们想要的是即使他从未登录过,也只显示所有用户一次,并且只显示最近的登录(即max(timestamp))。

有没有办法在一个SQL语句中执行此操作?

我们正在使用MSSQL 2005。

先谢谢你们,吉姆

4 个答案:

答案 0 :(得分:2)

SELECT tblUsers.Name, MAX(tblLogin.timestamp)
FROM 
tblUsers LEFT JOIN tblLogin ON tblUsers.ID = tblLogin.UserID
GROUP BY tblUsers.ID

答案 1 :(得分:2)

根据经验,以下查询通常快几倍

select 
    u.name, 
    l1.ip, 
    l1.timestamp, 
    l1.browser, 
    l1.os
from 
    tblUsers u
inner join 
    tblLogin l1 
on 
    u.id = l1.userid
    and l1.Id = ISNULL(
        (select 
            top 1 l2.id 
        from 
            tblLogin l2 
        where 
            u.id = l2.userid 
        order by 
            timestamp desc), 0)

比这个查询:

select *
from (
    select u.name, l.ip, l.timestamp, l.browser, l.os,
      row_number() over (partition by u.id order by timestamp desc) rn 
    from tblUsers u
    inner join tblLogin l on u.id = l.userid
) sub
where rn = 1

有一段时间我对这个主题特别感兴趣,因为我有一个巨大的(几百万行)表,我需要以类似的方式处理。所以我设置了一个测试两种方式,更快的查询运行大约20秒,而较慢的查询运行大约3分15秒。 (这是在SQL 2005上)。你的设置当然可能有所不同,这也取决于索引,但如果性能对你很重要,我会以两种方式测试并选择性能更好的。

通常的免责声明:我实际上没有运行上面的查询,它是为了说明这个想法,可能会出现一些语法错误。

答案 2 :(得分:1)

;WITH cLogins AS
(
  SELECT
     L.ip, M.LastSeen, L.browser, L.os
  FROM
      (SELECT UserID, MAX(timestamp) AS LastSeen FROM tblLogin GROUP BY UserID) M
      LEFT JOIN
      tblLogin L ON M.UserID = L.UserID AND M.LastSeen = L.JOIN 
)
SELECT
  I.Name, L.ip, L.LastSeen, L.browser, L.os
FROM 
  tblUsers U
  LEFT JOIN
  cLogins L ON U.UserID = L.UserID

答案 3 :(得分:1)

我认为,最易读的方法是使用row_number()。您可以使用它来为每个用户编号,从1开始,如:

select *
from (
    select u.name, l.ip, l.timestamp, l.browser, l.os,
      row_number() over (partition by u.id order by timestamp desc) rn 
    from tblUsers u
    inner join tblLogin l on u.id = l.userid
) sub
where rn = 1

rn = 1上的过滤器为每个用户提供最新行。子查询是必需的,因为SQL Server 2005不允许您在row_number()子句中引用where

执行此操作的最有效方法取决于每个用户的登录量。您可以在this blog post中找到一些更高级方法的详细解释。