【问题标题】:Ensure SQLite table only has one row确保 SQLite 表只有一行
【发布时间】:2015-10-13 13:34:15
【问题描述】:

如何强制表格只有一行?以下是我尝试过的。 UPDATE 触发器可能会起作用,但是,CREATE 触发器肯定不会。对于 CREATE,我想使用 SET,但是 SQLite 不支持 SET

CREATE TABLE IF NOT EXISTS `config` (
  `id` TINYINT NOT NULL DEFAULT 0,
  `subdomain` VARCHAR(45) NOT NULL,
  `timezone` CHAR(3) NOT NULL,
  `timeout` TINYINT NOT NULL,
  `offline` TINYINT NOT NULL,
  `hash_config` CHAR(32) NOT NULL,
  `hash_points` CHAR(32) NOT NULL,
  PRIMARY KEY (`id`));

INSERT INTO config(id,subdomain,timezone,timeout,offline,hash_config,hash_points) VALUES(0,'subdomain','UTC',5,0,'hash_config','hash_points');

CREATE TRIGGER `config_insert_zero`
BEFORE INSERT ON `config`
FOR EACH ROW
BEGIN
   -- SET NEW.id=0;
   NEW.id=OLD.id;
END;

CREATE TRIGGER `config_update_zero`
BEFORE UPDATE ON `config`
FOR EACH ROW
BEGIN
   -- SET NEW.id=0;
   NEW.id=OLD.id;
END;

【问题讨论】:

    标签: sql sqlite


    【解决方案1】:

    在一般情况下,要限制表中的行数,您必须阻止任何进一步的插入。 在 SQLite 中,这是通过 RAISE():

    完成的
    CREATE TRIGGER config_no_insert
    BEFORE INSERT ON config
    WHEN (SELECT COUNT(*) FROM config) >= 1   -- limit here
    BEGIN
        SELECT RAISE(FAIL, 'only one row!');
    END;
    

    但是,如果限制是一,您可以简单地将主键限制为固定值:

    CREATE TABLE config (
        id INTEGER PRIMARY KEY CHECK (id = 0),
        [...]
    );
    

    【讨论】:

    • 我喜欢你的第二个解决方案!感谢您对RAISE 的解释。并不是说我需要使用您的第二个解决方案的触发器,UPDATE 约束将如何强制执行?我如何使用CREATE TRIGGER config_update_zero BEFORE UPDATE ON config FOR EACH ROW BEGIN NEW.id=OLD.id; END;NEW.id=OLD.id; 展示它?
    • 您需要 UPDATE 触发器来做什么?
    • 不想允许UPDATE config SET id=2。当然,您的第二个解决方案可以防止这种情况发生,并且只是好奇如何使用触发器来完成。
    • 当我将约束更改为 id = 1 时,第二个优雅的解决方案对我有用 - 希望这是正确的(“行被分配连续递增的 rowid 值,从 1 开始,按照它们的顺序由 SELECT 语句返回。”来自sqlite.org/lang_createtable.html#rowid)
    • @Hugo 您可以使用任何固定值,只要您的应用程序实际插入它。当您无论如何只有一行并且必须特别处理表时,依赖自动增量行为没有多大意义。
    【解决方案2】:

    您可能要考虑的一个想法是让表格看起来只有一行。实际上,您保留所有先前的行,因为您很可能有一天想要保留所有过去值的历史记录。

    由于只有一行,因此确实不需要 ID 列,其目的是唯一区分每一行。但是,您确实需要一个时间戳,用于标识“一行”,该行将是写入表的最新行。

    CREATE TABLE `config_history` (
      `created`   timestamp default current_timestamp,
      `subdomain` VARCHAR(45) NOT NULL,
      `timezone` CHAR(3) NOT NULL,
      `timeout` TINYINT NOT NULL,
      `offline` TINYINT NOT NULL,
      `hash_config` CHAR(32) NOT NULL,
      `hash_points` CHAR(32) NOT NULL,
      PRIMARY KEY (`created`)
    );
    

    由于您通常只对写入的最后一行(最新版本)感兴趣,因此查询选择具有最新创建日期的行:

    select ch.created effective_date, ch.subdomain, ch.timezone, ch.timeout,
           ch.offline, ch.hash_config, ch.hash_points
    from   config_history ch
    where  ch.created =(
           select  max( created )
           from    config_history );
    

    在此查询前面放置一个create view config as,您将获得一个视图,该视图仅从表中选择一行,即最新的一行。对视图的任何查询都会返回一行:

    select  *
    from    config;
    

    视图上的instead of 触发器可以将更新转换为插入——您实际上并不想更改值,只需使用新值写入新行。这将成为新的“当前”行。

    现在您拥有一个看起来只有一行的表格,但您还保留了过去对该行所做的所有更改的完整历史记录。

    【讨论】:

      猜你喜欢
      • 2021-01-17
      • 1970-01-01
      • 1970-01-01
      • 2014-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-01
      • 1970-01-01
      相关资源
      最近更新 更多