【问题标题】:SQL Server - update one table with first and last rows from another tableSQL Server - 使用另一个表中的第一行和最后一行更新一个表
【发布时间】:2008-11-25 18:50:56
【问题描述】:

我有几个表用于记录应用程序的用户活动。表格看起来像这样(内存中的伪代码,可能在语法上不正确):

create table activity (
  sessionid uniqueidentifier not null,
  created smalldatetime not null default getutcdate()
);

create table activity_details (
  sessionid uniqueidentifier not null,
  activity_description varchar(100) not null,
  created smalldatetime not null default getutcdate()
);

我的目标是为报告目的填充一个汇总表,如下所示:

create table activity_summary (
  sessionid uniqueidentifier not null,
  first_activity_desc varchar(100) not null,
  last_activity_desc varchar(100) not null
);

第一个和最后一个活动描述将按时间顺序确定。我最初的想法是像这样更新汇总表:

truncate table activity_summary;

insert into activity_summary (sessionid)
select sessionid from activity;

update table activity_summary set
  first_activity_desc = (select top 1 activity_desc from activity_detail where sessionid = as.sessionid order by created asc),
  last_activity_summary = (select top 1 activity_desc from activity_detail where sessionid = as.sessionid order by created desc)
from activity_summary as;

但是,这对我来说似乎非常冗长且不必要。我只是不确定如何缩小它。我的直觉是我可以在插入语句中以某种方式完成所有操作,但我很难过。有什么建议么?

【问题讨论】:

    标签: sql sql-server sql-server-2005


    【解决方案1】:

    可能还有更有效的方法可以做到这一点,但这最接近您的原始方法:

    truncate table activity_summary;
    
    insert into activity_summary (sessionid, first_activity_desc, last_activity_summary)
    select a.sessionid
    ,(select top 1 ad.activity_desc from activity_detail AS ad where ad.sessionid = a.sessionid order by ad.created asc) AS first_activity_desc
    ,(select top 1 ad.activity_desc from activity_detail AS ad where ad.sessionid = a.sessionid order by ad.created desc) AS last_activity_summary
    from activity AS a;
    

    这样的事情可能更有效:

    truncate table activity_summary;
    
    WITH firsts AS (
        SELECT ad.sessionid
            ,ad.activity_desc
            ,ROW_NUMBER() OVER (ORDER BY ad.created ASC) as RowNumber
        FROM activity_detail AS ad
    )
    ,lasts AS (
        SELECT ad.sessionid
            ,ad.activity_desc
            ,ROW_NUMBER() OVER (ORDER BY ad.created DESC) as RowNumber
        FROM activity_detail AS ad
    )
    insert into activity_summary (sessionid, first_activity_desc, last_activity_summary)
    select a.sessionid
        ,firsts.activity_desc
        ,lasts.activity_desc
    from activity AS a
    INNER JOIN firsts ON firsts.sessionid = a.sessionid AND firsts.RowNumber = 1
    INNER JOIN lasts ON lasts.sessionid = a.sessionid AND lasts.RowNumber = 1
    

    【讨论】:

    • 谢谢。我不知道为什么我没有想到这一点——我想我只是讨厌将 select 语句包装成这样的列值。在任何一种情况下,查询生成的潜在读取数量都是巨大的——这就是我希望减少的。
    • 我会为你做一个没有嵌套查询的。
    【解决方案2】:
    insert into activity_summary
        (sessionid, first_activity_desc, last_activity_desc)
    select
        agg.sessionid,
        adf.activity_description,
        adl.activity_description
    from
        (SELECT
             sessionid, MIN(created) as firstcreated, MAX(created) as lastcreated
        from
             activity_detail group by sessionid
        ) agg
        JOIN
        activity_details adf ON agg.sessionid = adf.sessionid AND agg.firstcreated = adf.created
        JOIN
        activity_details adl ON agg.sessionid = adl.sessionid AND agg.lastcreated = adl.created
    

    【讨论】:

    • 几乎是我想要的。我看到了一个很大的漏洞,两个活动描述完全有可能具有相同的时间戳。
    • 同样的问题适用于 top 和 subclause 方法。除非有另一列(例如身份)或创建的是日期时间(仍然不是万无一失的),否则没有信息可以打破相等的顶行和尾行
    • 你是对的。也许我应该在明细表中添加一个标识列,然后将其最小/最大。
    • 没有决胜局,但你不会得到额外的行数。
    • 啊,是的,需要注意的一点... Min/max/top 都给一行。将选择相等的顶部或相等的尾部行之一(实际上是随机的)
    【解决方案3】:

    或者,

    插入等

    (SELECT description FROM detail d1 WHERE d1.sessionid = a.sessionid
    不存在(从详细信息中选择 1,其中创建

    (SELECT description FROM detail d2 WHERE d2.sessionid = a.sessionid
    并且不存在(从详细信息 d2 中选择 1 在哪里创建 > d1.created))作为 desc2

    来自活动 a

    (我自己更喜欢这个。)

    【讨论】:

      【解决方案4】:

      大概,

      插入等

      SELECT a.sessionid, d1.activity_description, d2.activity_description
      
      FROM activity a
      
      JOIN detail d1 ON a.sessionid = d1.sessionid
      JOIN detail d2 ON a.sessionid = d2.sessionid
      
      WHERE NOT EXISTS  
        (SELECT 1 FROM detail WHERE sessionid = a.sessionid AND created < d1.created)
      
      AND NOT EXISTS  
        (SELECT 1 FROM detail WHERE sessionid = a.sessionid AND created > d2.created)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-12
        • 1970-01-01
        • 2021-11-12
        • 1970-01-01
        • 2019-03-09
        • 1970-01-01
        • 2021-04-04
        • 2012-01-07
        相关资源
        最近更新 更多