【问题标题】:SQL: select most recent event of certain typeSQL:选择某种类型的最新事件
【发布时间】:2012-01-20 00:30:24
【问题描述】:

我的数据库 (sqlite3) 有两个表,其架构如下:

CREATE TABLE log(d date, usr text, tag text, bytes int);
CREATE TABLE valid(tag text, filesize int);

假设我有以下示例数据:

      d    | usr  |  tag | bytes
--------------------------------
2012-01-19 | bob  |  foo | 990
2012-01-18 | bob  |  foo | 1000
2012-01-17 | joe  |  bar | 2000
2012-01-16 | joe  |  bar | 1800
2012-01-15 | joe  |  baz | 0


tag  | size
-----------
foo  | 1000
bar  | 2000

我想检索导致用户无法访问有效文件中的所有字节的最新事件列表。在上面的例子中,选择应该产生 2012-01-19 |鲍勃 |富 | 990.

现在,我正在使用两个 select 语句来获取结果。第一个获取每个用户的最新事件,第二个检查是否访问了所有字节。

CREATE VIEW tmp AS 
SELECT * FROM log JOIN (SELECT max(d) AS maxd, usr FROM log GROUP BY usr) AS 
tmplog ON (tmplog.usr=log.usr and tmplog.maxd=log.d);

SELECT usr,d FROM tmp 
WHERE tag IN (SELECT tag FROM valid) AND bytes NOT IN (SELECT size FROM valid);

有没有办法通过一个选择或更有效地做到这一点?

更新:

在上面的示例中,查询不应检索用户“joe”,因为他最近的日志条目显示正在访问的完整文件。

【问题讨论】:

    标签: sql sqlite


    【解决方案1】:

    根据usrtag(其中bytes < filesize)查找最新的d,然后选择与最新的d/usr/tag 值匹配的log 条目:

        SELECT log.*
          FROM log
    INNER JOIN (    SELECT usr, tag, MAX(d) AS d
                      FROM log
                  GROUP BY 1, 2) most_recent
            ON most_recent.usr = log.usr
                AND
               most_recent.tag = log.tag
                AND
               most_recent.d = log.d
    INNER JOIN valid
            ON log.tag = valid.tag
         WHERE log.bytes < valid.filesize;
    

    【讨论】:

    • 在上面的示例中返回“joe”,但我想排除它,因为他最近的访问是成功的。
    • 对查询的一个小调整让一切正常。我从第二个选择中删除了“标签”及其相关的比较。
    【解决方案2】:
    SELECT log.* FROM
      log
      JOIN
      (SELECT usr,tag,MAX(d)
       FROM log
       GROUP BY usr,tag) log2
      USING(usr,tag)
      JOIN valid ON (log.tag = valid.tag)
      WHERE d=`max(d)` AND bytes != valid.filesize
    

    【讨论】:

      【解决方案3】:
      Select
      log.*
      From log
      Inner Join valid On valid.tag = log.tag
      Where log.bytes < valid.size Order By log.d Desc
      

      应该可以,但在我看来效率不高。

      略有不同的是,如果同一用户有多个问题,它将为每个用户返回多行 - 您是否特别不想要这个?

      【讨论】:

      • 我在一个更大的数据集上尝试了这个,它只返回了一些应该返回的记录(最近的)。我还希望每个用户/文件只有一行。
      【解决方案4】:

      到目前为止我可以建议的最接近的:

      1. 加入logvalid 以获取bytes &lt;= size 所在的所有行,并根据tag 找出MAX(d)

      2. tagd 上将结果集加入到log 以获取与相应事件相关的整行。

      3. 再次加入valid 以仅获取bytes &lt; size 所在的行。这对于过滤掉字节匹配大小的最大日期是必要的。

      这可能是查询的样子:

      SELECT
        log.*
      FROM log
        INNER JOIN valid ON log.tag = valid.tag AND log.bytes < valid.size
        INNER JOIN (
          SELECT
            log.tag,
            MAX(log.d) AS d
          FROM log
            INNER JOIN valid ON log.tag = valid.tag AND log.bytes <= valid.size
          GROUP BY
            log.tag
        ) last
          ON log.tag = last.tag AND log.d = last.d
      

      以上假设在任何日期对于特定标签只能有一个事件。否则,如果在同一日期发生多个事件,则问题中提供的架构不允许人们确定哪个事件是最新事件。

      【讨论】:

      • 这返回了“错误:不明确的列名:标签”,所以我在第二次选择中使用了 log.tag。在那之后,我只得到了一个结果,最近的一个,但没有其他任何结果。
      • @Octave:是的,那是因为我忘了添加GROUP BY ...。抱歉,更新了我的答案。
      • 我认为查询不考虑最近的事件是否“好”。在示例数据中,不应选择用户“joe”。
      • @Octave:在这种情况下,应该有一种方法可以确定特定标签的某个事件是否确实是最新的,以防多个事件发生在同一个d 上。假设目前后者是不可能的,我可以建议改变我目前的解决方案。请查看我的更新。
      • 你是对的。我将示例简化为仅包含日期,但实际上我使用的是日期时间。我的主键是 (d, usr, tag),在我的系统中不能有多个此类事件。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-01
      • 2013-06-20
      • 1970-01-01
      • 2019-08-14
      • 1970-01-01
      • 2011-01-12
      相关资源
      最近更新 更多