【问题标题】:Add constraint to existing SQLite table向现有 SQLite 表添加约束
【发布时间】:2017-09-04 20:46:24
【问题描述】:

我正在使用 SQLite,它 doesn't supporta constraint 添加到现有表中。

所以我不能这样做(仅作为示例):

ALTER TABLE [Customer]
ADD CONSTRAINT specify_either_phone_or_email
CHECK (([Phone] IS NOT NULL) OR ([Email] IS NOT NULL));

是否有针对这种情况的解决方法?

我知道:

  • 我可以为新表添加约束,但它不是新表(它是由我的 ORM EF Core 生成的)
  • 我可以进行“表重建”(重命名表、创建新表、复制旧数据、删除临时表),但这似乎很复杂

想法

  • 我能否以某种方式将该表的副本复制到一个新表中,并进行一些架构更改?
  • 或者以某种方式“获取”架构,并在 SQL 脚本中对其进行编辑,然后使用该架构添加一个表?

【问题讨论】:

    标签: sqlite database-schema entity-framework-core alter-table check-constraints


    【解决方案1】:

    要复制带有一些架构更改的表,您必须手动创建和复制:

    BEGIN;
    CREATE TABLE Customer_new (
        [...],
        CHECK ([...])
    );
    INSERT INTO Customer_new SELECT * FROM Customer;
    DROP TABLE Customer;
    ALTER TABLE Customer_new RENAME TO Customer;
    COMMIT;
    

    要读取架构,请在sqlite3 command-line shell 中执行.schema Customer。 这为您提供了 CREATE TABLE 语句,您可以对其进行编辑和执行。


    要原地更换表,可以使用后门。

    首先,阅读实际的表定义(这与您从.schema 得到的相同):

    SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'Customer';
    

    将您的 CHECK 约束添加到该字符串,然后使用 PRAGMA writable_schema=1; 启用对 sqlite_master 的写访问并将新表定义写入其中:

    UPDATE sqlite_master SET sql='...' WHERE type='table' AND name='Customer';
    

    然后重新打开数据库。

    警告:这仅适用于不会更改表的磁盘格式的更改。如果您确实进行了更改记录格式的任何更改(例如添加/删除字段,或修改 rowid,或添加需要内部索引的约束),您的数据库将严重崩溃。

    【讨论】:

    • 在我的情况下使用 CLI 是不切实际的,因为我正在尝试以编程方式进行操作,并且架构是由 EF Core 生成的。但是,“后门”选项很有趣。假设我在 ORM 创建表后直接运行它,并且我所做的只是像您解释的那样添加检查约束,它应该是安全的吗?
    • 您可以随时运行它。但是,如果表是空的,那么简单地删除并重新创建它(就像第一个选项一样)就不那么危险了。
    • 删除并重新创建它意味着我必须手动编写 sql,而使用后门意味着我可以依靠 ORM 来正确处理它,并确保编译时安全。在这种情况下,这是一个有吸引力的选择。谢谢!
    • 我应该在完成后做一个PRAGMA writable_schema=0; 吗?文档不清楚它是否在交易后仍然存在。
    • 你可以重新执行你从SELECT sql FROM sqlite_master得到的东西。
    猜你喜欢
    • 2013-03-08
    • 2014-05-11
    • 1970-01-01
    • 2022-10-04
    • 2013-09-06
    • 2018-02-20
    • 2021-07-22
    • 2021-05-19
    相关资源
    最近更新 更多