如何在SQLite中实现非PK长字段的自增

问题描述 投票:0回答:1

我有一段数据库模式,它对于 SQL Server 来说运行得非常好:

builder.Property(b => b.FieldName)
    .UseIdentityColumn(1, 1)
    .ValueGeneratedOnAdd();

但是当我尝试切换到 SQLite 进行测试时,我得到了错误:

SQLite Error 19: 'NOT NULL constraint failed: TableName.FieldName'

根据我的理解,

ValueGeneratedOnAdd
在SQLite中不适用于非PK字段(在SQL Server中它会自动递增)。

有什么解决方法可以解决这个问题吗?

c# sqlite entity-framework
1个回答
0
投票

注意此答案不提供 EntityFramework 解决方案,而是尝试提供 SQLite 如何处理自动生成标识特定行的唯一值的背景。

这个答案可能有用如何使用 EF core 在 SQLite 中创建自动增量列?

有什么解决方法可以解决这个问题吗? 是的,从理论上讲,可能与模式无关!

SQLite 非常灵活,因此与其他数据库有很大不同。

可能什么也不做的基础

除了虚拟表和WITHOUT ROWID 表之外的所有表的SQLite 都有一列是唯一生成的整数值。这是 rowid 列。该列是隐藏列,因此需要显式引用。

但是@#$% 自动增量?

SQLite 致力于能够使用 SQL 从其他数据库创建模式。已利用 AUTOINCRMENT 关键字/约束来更接近地模仿 AUTOINCRMENT 的有意使用,尽管歪曲了其用途。

当使用 AUTOINCRMENT 时,对于定义为 INTEGER PRIMARY KEY 的列,它必须是(每个表只能存在 1 个这样的列)。使用时,约束/规则是,如果自动生成的值“必须”大于任何现有或曾经使用过的值。即保证价值会增加。

    如果省略自动增量,则生成的值(如果已达到最高可能值)可能是较低的未使用值。但是,在相同的情况下(已使用尽可能高的值),则会导致 SQLITE_FULL 异常。
  • 如果编码了 INTEGER PRIMARY KEY(带或不带 AUTOINCRMENT)(请注意,例如关键字必须是 INTEGER 而不是 INT),则该列将是
  • rowid

    列的别名。

  • 因此,理论上您可以只使用
rowid

并跳过 .ValueGeneratedOnAdd();

但是
,当需要该值时,通过指定 rowid (或 oidrowid)来提取它 . 或者,您可以指定一个整数列作为主键并使其可为空(如果可能的话,即您可能不希望 NOT NULL,因为包装器在插入时倾向于使用 null)。

这里有一个演示,展示了 SQLite 生成的一些工作原理:-

DROP TABLE IF EXISTS a; CREATE TABLE IF NOT EXISTS a (c1); /* Insert 2 rows where the value in column c1 has nothing at all to do with the rowid */ INSERT INTO a VALUES('anything'); INSERT INTO a VALUES('yet another anything'); /* Now change the rowid(s) to 100 times what they were */ UPDATE a SET rowid = rowid * 100; /* now insert 10 rows via recursive CTE */ WITH cte1(i) AS (SELECT 1 UNION ALL SELECT cte1.i+1 FROM cte1 WHERE i < 10 LIMIT 10) INSERT INTO a SELECT 'xxx'||(i*100) FROM cte1; /* play around with the maximum possible rowid */ /* add 2 rows one at max the other with generated value NOTE!! no AUTOINCREMENT */ INSERT INTO a (rowid,c1) VALUES (9223372036854775807,'maxrowid'), /* specify the MAX possible value for this row */ (null,'higher then max rowid?????'); /* null!!!!???? SQLite allows null and then generates the value */ /* add another row with generated rowid */ INSERT INTO a (c1) VALUES('another higher than max rowid!!!!!'); /* Output as per typicalll all columns query */ SELECT * FROM a; /* now add the hidden rowid column to the output */ SELECT *,rowid, oid, _rowid_ FROM a; /*<<<<<<<<<< TABLE B ROWID ALIAS >>>>>>>>>>*/ /* Add table B now with a column rowid_alias as an alias of the rowid column */ DROP TABLE IF EXISTS b; CREATE TABLE IF NOT EXISTS b (rowid_alias INTEGER PRIMARY KEY, c1); /* Populate table b from column c1 only of table a (i.e. rowid and thus alias generated) */ INSERT INTO b (c1) SELECT c1 FROM a; /* Now show both table a and table b JOINED BY the c1 column */ SELECT b.*, b.rowid AS rowid_from_b, a.*, a.rowid AS rowid_from_a FROM b JOIN a ON b.c1 = a.c1; /*<<<<<<<<<< TABLE C - AUTOINCREMENT >>>>>>>>>>*/ /* Show the schema for prior to table c */ DROP TABLE IF EXISTS c; CREATE TABLE IF NOT EXISTS c (rowid_alias INTEGER PRIMARY KEY AUTOINCREMENT, c1); INSERT INTO c SELECT rowid,* FROM a UNION SELECT * FROM b; SELECT * FROM c; SELECT * FROM sqlite_sequence; INSERT INTO b (c1) VALUES('rowid still has max'); /* NOTE COMMENT OUT THE NEXT LINE IF ENVIRONMENT IS TO BE CLEANED */ INSERT INTO c (c1) VALUES('what about AUTOINCREMENT when max rowid has been used'); DELETE FROM sqlite_sequence; DROP TABLE IF EXISTS a; DROP TABLE IF EXISTS b; DROP TABLE IF EXISTS c;

运行时(故意包含 SQLITE_FULL 失败),会产生以下输出:-

结果 1

表中的典型摘录 a:-

即没什么特别的
结果 2

表 a 以及隐藏的 rowid 列:-

即通过对标准表不执行任何操作,可以通过 rowid、oid 或
    rowid
  • 引用自动生成的值。 请注意,如果已使用 rowid 的最高值,则如何使用其他未使用的随机值生成 rowid 的 1616919119006865753 和 2751465668745177295 。不适用的是使用自动增量(不是 auto_increment)。
  • 因为只有一个 PK 索引,所以在没有 ORDER BY 的情况下,是按照 PK 又名 rowid 的顺序提取的。使用 ORDER BY 和索引显然是明智的。
结果 3

表 b 具有 INTEGER PRIMARY KEY,因此是 rowid 列和通过 c1 列连接的表 a 的别名。

结果 4

表 c 具有自动增量功能,其中加载了表 a 和表 c 中的行。注意没有生成值。 :-

结果 5

自动增量使用的 sqlite_sequence :-

消息/错误

由于在使用了可能的最高生成值时尝试插入表c:- /* NOTE COMMENT OUT THE NEXT LINE IF ENVIRONMENT IS TO BE CLEANED */ INSERT INTO c (c1) VALUES('what about AUTOINCREMENT when max rowid has been used') > database or disk is full > Time: 0s

注意该消息已被使用的 SQLite 工具 (Navicat) 更改/处理。
  • 您不妨参考
https://www.sqlite.org/autoinc.html

最后一个警告,不使用 rowid(整数主键)的别名可能会导致以下问题:-

  • VACUUM 命令可能会更改任何没有显式 INTEGER PRIMARY KEY 的表中条目的 ROWID。

© www.soinside.com 2019 - 2024. All rights reserved.