【问题标题】:Limit an sqlite Table's Maximum Number of Rows限制一个 sqlite 表的最大行数
【发布时间】:2011-01-03 09:18:45
【问题描述】:

我希望实现一种“活动日志”表,其中用户执行的操作存储在 sqlite 表中,然后呈现给用户,以便他们可以看到他们所做的最新活动。但是,自然地,我觉得没有必要保留所有历史记录,所以我想知道是否有一种方法可以配置表以在达到最大设置限制后开始修剪旧行。

例如,如果限制为 100,这就是表中当前的行数,当插入另一个操作时,最旧的行会自动删除,因此最多始终有 100 行。有没有办法配置 sqlite 表来做到这一点?还是我必须运行一个 cron 作业?

澄清编辑:在任何给定时刻,我都想显示表格的最后 100 个(例如)操作/事件(行)。

【问题讨论】:

    标签: database database-design sqlite database-table


    【解决方案1】:

    有几种方法可以将表限制为 100 行。 (为简洁起见,以下代码为 5 行。)在 SQLite 版本 3.7.9 中测试。

    所有这些代码都依赖于 SQLite 处理数据类型声明的方式中的一种怪癖。 (无论如何,这对我来说似乎很古怪。)SQLite 允许您将诸如 3.14159 和“wibble”之类的废话插入到一个裸整数列中。但它只允许您将整数插入声明为integer primary keyinteger primary key autoincrement 的列中。

    外键约束

    对有效 id 编号表使用外键约束,以保证 id 编号在您想要的范围内。外键约束甚至适用于自动递增列。

    pragma foreign_keys=on;
    create table row_numbers (n integer primary key);
    
    insert into row_numbers values (1);
    insert into row_numbers values (2);
    insert into row_numbers values (3);
    insert into row_numbers values (4);
    insert into row_numbers values (5);
    
    create table test_row_numbers (
      row_id integer primary key autoincrement,
      other_columns varchar(35) not null,
      foreign key (row_id) references row_numbers (n)
    );
    
    insert into test_row_numbers (other_columns) values ('s');
    insert into test_row_numbers (other_columns) values ('s');
    insert into test_row_numbers (other_columns) values ('s');
    insert into test_row_numbers (other_columns) values ('s');
    insert into test_row_numbers (other_columns) values ('s');
    

    第六次插入失败,出现“错误:外键约束失败”。

    我不认为使用自动增量是完全安全的。在其他平台上,回滚会在序列中留下间隙。如果您不使用自动增量,则可以通过从“row_numbers”中选择 id 号来安全地插入行。

    insert into test_row_numbers values
    (
      (select min(n) 
       from row_numbers 
       where n not in 
         (select row_id from test_row_numbers)), 
      's'
    );
    

    CHECK() 约束

    下面的主键约束保证 id 数字是整数。 CHECK() 约束保证整数在正确的范围内。您的应用程序可能仍需要处理回滚导致的间隙。

    create table test_row_numbers (
      row_id integer primary key autoincrement,
      other_columns varchar(35) not null,
      check (row_id between 1 and 5)
    );
    

    【讨论】:

      【解决方案2】:

      另一种解决方案是预先创建 100 行,而不是 INSERT 使用 UPDATE 来更新最旧的行。
      假设表有datetime字段,查询

      UPDATE ...
      WHERE datetime = (SELECT min(datetime) FROM logtable)
      

      可以胜任。

      编辑:显示最后 100 个条目

      SELECT * FROM logtable
      ORDER BY datetime DESC
      LIMIT 100
      

      更新:这是一种使用连接操作创建 130 个“虚拟”行的方法:

      CREATE TABLE logtable (time TIMESTAMP, msg TEXT);
      INSERT INTO logtable DEFAULT VALUES;
      INSERT INTO logtable DEFAULT VALUES;
      -- insert 2^7 = 128 rows
      INSERT INTO logtable SELECT NULL, NULL FROM logtable, logtable, logtable,
         logtable, logtable, logtable, logtable;
      UPDATE logtable SET time = DATETIME('now'); 
      

      【讨论】:

      • 我想我错过了一些东西,但那不是总是只更新最后一行吗?我想我忘了在我的原始帖子中提及,但我想展示例如最后 5 个动作/事件,无论它们有多大,而不仅仅是最后一个。不过还是谢谢。
      • @Blaenk:假设您的更新将 datetime 字段设置为它所触及的每一​​行的当前时间,这应该按时间顺序循环遍历各行。
      • 哦,然后当然还要更新 datetime 字段,这将使它成为最新的行,对吧?我知道了。嗯,这很有趣。有没有一种简单的方法可以预先创建一定数量的“虚拟”行?
      • @Blaenk,我不知道您是否可以在 SQLite 中通过查询插入 100 个虚拟行。当然,您可以使用代码来做到这一点。在获取最后 N 行时,请查看我的编辑。
      • 虽然您的虚拟行生成效果很好,但它会为所有行创建相同的日期,从而终止您仅更新最旧记录的方法(所有将使用您的查询进行更新)。类似于 UPDATE 日志 SET time = DATETIME(DATETIME('now'), '+'||rowid||'minutes');为我解决了这个问题。
      【解决方案3】:

      您可以创建一个在 INSERT 时触发的 trigger,但解决此问题的更好方法可能是简单地安排一个定期运行(例如每周一次)并从表中删除记录的计划作业。

      【讨论】:

      • 哦,好吧,这就是我的想法(cron),但我想确保没有针对这种情况的内置机制。
      • 如果您熟悉触发器,能否提供一个在这种情况下有用的示例?也就是说,如果它们不是太复杂,如果它们是,不要担心。
      猜你喜欢
      • 1970-01-01
      • 2012-03-23
      • 2010-12-05
      • 2022-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-31
      • 2015-03-09
      相关资源
      最近更新 更多