【问题标题】:How to set a max items for a specific table using Trigger, in SQLite?如何在 SQLite 中使用触发器为特定表设置最大项目?
【发布时间】:2020-06-07 07:37:52
【问题描述】:

这是一个简单的问题。

背景

我应该在某个表中最多有 400 行,基于时间戳字段,所以旧的将被自动删除。在这里,假设它是 3。

表格有各种字段,但时间戳在这里很重要。

问题

即使我成功了(看起来here),由于某种原因,它让我最多获得了一个额外的项目,所以我只是相应地调整了它。这意味着我得到了 4 个项目,而不是 3 个。

private const val MAX_ITEMS = 3
private val TIMESTAMP_FIELD = "timestamp"


private val DELETE_FROM_CALL_LOG_TILL_TRIGGER =
        String.format(
                "CREATE TRIGGER %1\$s INSERT ON %2\$s 
                 WHEN (select count(*) from %2\$s)>%3\$s 
                 BEGIN 
                 DELETE FROM %2\$s WHERE %2\$s._id IN " +
                        "(SELECT %2\$s._id FROM %2\$s ORDER BY %2\$s.$TIMESTAMP_FIELD DESC LIMIT %3\$d, -1);
                 END;"
                , "delete_till_reached_max", TABLE_NAME, MAX_ITEMS - 1)

我尝试过的

我试过了:

  • 将条件更改为只是插入(意味着没有WHEN 部分)
  • LIMIT %3\$d, -1 更改为 LIMIT -1 OFFSET %3\$d 。还尝试了与“-1”不同的数字(尝试了 0,因为我认为它是多余的)。

问题

  1. 为什么我必须使用 MAX_ITEMS - 1 而不仅仅是 MAX_ITEMS ?为什么它给我留下 4 件而不是 3 件?
  2. 我有WHEN 有关系吗?好点了吗?

【问题讨论】:

  • 如果有 400 个项目不是绝对关键,您也许可以从 rowid
  • @CaiusJard 为什么要使用 ID?它总是在增加......
  • 呃,是的.. 我以为这就是您要问的 - 维护 400 个项目的更好/更简单的方法是使用递增数字并删除所有 LTE 当前数字减去 400。这意味着删除可以用数学来完成,你很容易知道(刚刚插入的记录的 id),而不必按日期 desc 对 N 个项目进行排序,取第 400 个项目的日期并删除所有 LTE 它
  • 如果ID一直在增加,SQLite只支持FOR EACH ROW触发器,那么我们说的是删除一行。这是插入前触发器中的 delete from mytable where id = (select min(id) from mytable group by null having count(*) = 400) 或后行触发器中的 > 400
  • 各位,我应该从事的领域不是 ID。它是时间戳(长类型)。我需要从该字段中删除最旧的,即具有最低值的那些。如果我使用 ID,那么只有在我总是添加具有较新 ID 的新项目并按此类排序时才有意义。

标签: sql sqlite android-sqlite


【解决方案1】:
  1. 您省略了BEFORE | AFTER 子句,因此默认为BEFORE。这意味着您正在计算插入之前的行数,而不是插入之后的行数。
  2. 这取决于。起初,当表还没有达到限制时,快速计数查找可能会为您节省一些时间,因为您可以避免更复杂的删除。但是一旦桌子满了,无论如何你都必须删除,所以计数只是额外的工作。

这应该可行:

private const val MAX_ITEMS = 3
private val TIMESTAMP_FIELD = "timestamp"

private val DELETE_FROM_CALL_LOG_TILL_TRIGGER =
  String.format(
    "CREATE TRIGGER %1\$s AFTER INSERT ON %2\$s 
     FOR EACH ROW
     BEGIN 
       DELETE FROM %2\$s WHERE _id =
         (SELECT _id FROM %2\$s ORDER BY %4\$s DESC LIMIT 1 OFFSET %3\$s);
     END;"
    , "delete_till_reached_max", TABLE_NAME, MAX_ITEMS, TIMESTAMP_FIELD)

一旦表中有 400 行,您也可以调用类似 trg_keep_rowcount_constant 的触发器并从代码中删除 GROUP BY null HAVING COUNT(*) > %3\$s

演示:https://dbfiddle.uk/?rdbms=sqlite_3.27&fiddle=ea3867e20e85927a2de047908771f4f1

【讨论】:

  • 那你能出示正确的吗?至于计数,由于最终会达到最大值,您是否建议删除计数?这不是一个相对快速的检查吗?不是 O(1) 吗?
  • 触发器看起来正确。使用limit 400, 1 删除最旧的行。 (您可以将 IN 更改为 =,因为您要删除的行不会超过一行。)我会删除 WHEN 子句,因为一旦您达到 400 行,它将始终是 400 行,并且WHEN 子句只是增加了不必要的工作。为了计算行数,必须读取整个表。只有 400 行,这并不算多,但这是可以避免的工作。
  • 真的吗?我认为它有一个有多少的缓存结果,并且可以缓存它以供查询其余子句。无论如何,您可以在您建议的更改之后编写代码吗?当你写它们时很难得到它们......
  • 没有什么可以缓存的。触发器在每一行之后触发,因此每次都必须检索行数。至于你的触发器:它有效,对吧?所以你可以保持原样或删除WHEN 子句。我已经添加了触发器,并对我的答案进行了轻微更改。我明确表达了AFTER INSERTFOR EACH ROW。我还更改了IN 子句,实际上不知道它的性能是好是坏。您可以使用我的版本或保留您的版本或将两者混合。无论你喜欢什么:-)
  • 有些人讨厌触发器,我喜欢它们 :-) 是的,每次插入都需要额外的工作(如果使用另一个 DBMS,我会将其设为 after 语句触发器,而不是后行触发器),但是触发器保证一致性。始终遵守您的规则(最多 400 行,保留最新日期)。表中的行数永远不会超过 400 行,因为您的触发器可以确保这一点。
猜你喜欢
  • 2014-11-22
  • 1970-01-01
  • 2011-12-28
  • 1970-01-01
  • 2021-08-25
  • 1970-01-01
  • 2022-06-10
  • 2020-04-26
  • 1970-01-01
相关资源
最近更新 更多