【问题标题】:Select top one from left outer join从左外连接中选择前一个
【发布时间】:2010-11-20 00:08:42
【问题描述】:

伙计们,我有一个查询,基本上选择我们用户使用的最新浏览器。

这是我们的(简化的)表结构

HITS_TABLE
----------
USERID
BROWSER
HITSDATE

USER_TABLE
----------
USERID
USERNAME

这是我查询用户使用的最新浏览器的方式

SELECT U.*, H.BROWSER

FROM USER_TABLE U

CROSS APPLY 
  (SELECT TOP 1 BROWSER 
   FROM HITS_TABLE 
   WHERE HITS_TABLE.USERID = U.USERID
   ORDER BY HITS_TABLE.HITSDATE DESC
  )as H

HITS_TABLE 是几天前刚刚添加的。

因此,该查询只是在我们添加 HITS_TABLE 后访问我们网站的结果用户,并消除了其他用户。

这里是样例

USER_TABLE
-------------------
USERID     USERNAME
-------------------
1          'Spolski'
2          'Atwoord
3          'Dixon'


HITS_TABLE
------------------------------
USERID     HITSDATE     BROWSER
------------------------------
2          15/8/2009    'Firefox 3.5'
1          16/8/2009    'IE 6'
2          16/8/2009    'Chrome'

这是示例结果

------------------------------
USERID     USERNAME     BROWSER
------------------------------
1          'Spolsky'    'IE 6'
2          'Atwoord'    'Chrome'

但是,我想使用“未知”浏览器添加其他用户。 这是我想要的结果

------------------------------
USERID     USERNAME     BROWSER
------------------------------
1          'Spolsky'    'IE 6'
2          'Atwoord'    'Chrome'
3          'Dixon'      'Unknown'

我相信它可以通过 LEFT OUTER JOIN 来实现。 但我总是有这个:(我不想要这个结果)

------------------------------
USERID     USERNAME     BROWSER
------------------------------
1          'Spolsky'    'IE 6'
2          'Atwoord'    'Chrome'
2          'Atwoord'    'Firefox 3.5'
3          'Dixon'      'Unknown'

我希望我的问题很清楚。

【问题讨论】:

    标签: sql-server-2005 outer-join


    【解决方案1】:

    对 hits_table 使用 userid 上的 group by 允许您获取每个 userid 的 max() hitsdate。我在下面的代码中称之为 LATEST HITS。

    在带有 LATEST HITS 的左连接的 USER TABLE 上选择允许您为每个用户提取记录。

    重新加入 HITS TABLE 之后,您就可以提取与该日期关联的浏览器记录,或者为没有记录的用户提取 null。

    select
       user_table.userid,
       user_table.username,
       isnull(hitstable.browser, 'unknown') as browser
    from
      user_table
    left join
    (
      select
        userid,
        max(hitsdate) hitsdate
      from
        hits_table
      group by  
        userid
    ) latest_hits
    on
      user_table.userid = latest_hits.userid    
    left join
      hits_table
    on hits.table.userid = latest_hits.userid
    and hits_table.hitsdate = latest_hits.hitsdate
    

    【讨论】:

    • 这个解决方案考虑了一个重要的事实,即缺少其他的:如果 USERID 和 HITSDATE 的组合不明确,例如是否存在附加行(2, 16/8/2009, 'Safari')?使用排名函数,您会得到一个不确定的结果。你能说出选择了哪一个吗?该解决方案将提供两种组合,恕我直言。
    • 附加信息:有关 SQL Server 排名的信息,请参阅msdn.microsoft.com/en-us/library/ms189798%28SQL.90%29.aspx
    • 你是对的。 max() 函数对此非常有用。谢谢。但我认为它应该是左外连接。
    【解决方案2】:

    你不能子选择,不漂亮但应该工作..

    SELECT U.*,
    
    ISNULL((SELECT TOP 1 BROWSER 
       FROM HITS_TABLE 
       WHERE HITS_TABLE.USERID = U.USERID
       ORDER BY HITS_TABLE.HITSDATE DESC),'UnKnown') AS Browser
    
    FROM USER_TABLE U
    

    【讨论】:

    • 如果您想从该查询的 hits 表中访问除浏览器之外的任何其他列,那么子选择不适合您。在这种情况下,我会分析 @rwarren 和 @gbn 解决方案,看看哪个表现更好。 @Mao 有一个关于非确定性结果的有趣观点。戴上务实的帽子,您可能会通过将 Time 添加到 HITSDATE 来忽略这种极端情况。
    【解决方案3】:
    SELECT U.*,'BROWSER' = 
        case 
         when (SELECT TOP 1 BROWSER FROM HITS_TABLE WHERE HITS_TABLE.USERID = U.USERID ORDER BY HITS_TABLE.HITSDATE DESC) is  null then 'Unknown'
    else (SELECT TOP 1 BROWSER FROM HITS_TABLE WHERE HITS_TABLE.USERID = U.USERID ORDER BY HITS_TABLE.HITSDATE DESC)
        end
    FROM USER_TABLE U
    

    【讨论】:

    • 在您的解决方案中,当结果不为空时,子选择不会执行两次吗?首先评估“when”并找出浏览器不为null,其次在“else”中提取结果?
    猜你喜欢
    • 2018-03-30
    • 1970-01-01
    • 2011-05-17
    • 1970-01-01
    • 2012-10-31
    • 2017-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多