Sqlite错误:main.table_name不存在。

4
在我的UWP应用程序中,我正在创建sqlite db中的表格。此外,我编写了各种Alter命令,其中我检查安装的应用程序是否包含Customertbl中相应的列,并将Customertbl表格重命名为Customertbl_old,创建新表Customertbl,并最终将所有行从Customertbl_old表格存储到Customertbl表格中。然后,我删除了Customertbl_old表格。 现在,在此部分中一切都工作正常,但是,当我尝试在OwnerTbl中删除/插入一行时,它会抛出异常,说:Sqlite错误:main.Customertbl_old不存在。
//**Code where a column name is updated by creating new table**
string tableCommand = "PRAGMA table_info(Recordings)"; 
SqliteCommand createTable = new SqliteCommand(tableCommand, db);
SqliteDataReader query = createTable.ExecuteReader(); 
tableCommand = "PRAGMA foreign_keys = off; " + " 
BEGIN TRANSACTION; " +
" ALTER TABLE Customertbl RENAME TO _Customertbl_old; " +
" CREATE TABLE Customertbl ( " + " ID INTEGER PRIMARY KEY AUTOINCREMENT, " + " CustomerName NVARCHAR(100) NULL, " + " Password NVARCHAR(100) NULL, " + " 
pkID INTEGER NULL, " + " ActivityName NVARCHAR(255) NULL);" +
 " INSERT INTO Customertbl(Name,Password) " + " SELECT Name,Password " + " 
FROM _Customertbl_old ;" + " COMMIT; " + " PRAGMA foreign_keys=on; " + " 
DROP TABLE 
_Customertbl_old";

// **code where exception occur**
using (SqliteConnection db = new 
SqliteConnection("Filename=" + App.dbName)) 
{ 
db.Open(); 
SqliteCommand deleteCommand = new SqliteCommand(); 
try 
{ 
    deleteCommand.Connection = db; 
    deleteCommand.CommandText = "DELETE FROM Ownertbl where fkId = @id";  
    deleteCommand.Parameters.AddWithValue("@id", id);  
    deleteCommand.ExecuteReader()//here the exception occur ;  
    db.Close(); 
 } 
}

1
这是预期的,因为你删除了它?如果不了解架构的更多信息,很难确定问题出在哪里。同时提供导致错误的确切语句也会有所帮助。 - Fildor
2
你使用的是哪个版本的SQLite?最近的版本已经更新了ALTER TABLE命令,以自动重命名某些内容(包括PK引用)。这意味着旧的修改表格的方式(重命名、创建新表、移动数据)可能不再起作用,除非你使用pragma legacy_alter_table=ON。请参见ALTER TABLE。或者,你也可以直接修改表格。 - TripeHound
2
感谢大家,@CPerkins感谢您分享链接link,该链接解释了在sqlite表中重命名列的正确方法。这修复了异常。 :) - Shakita
1
感谢 TripeHound 首先分享了它。我只是努力做出一些进一步的澄清。很高兴它有所帮助。 - C Perkins
1
是的,但是在我的解决方法中,我没有使用@TripeHound提到的pragma legacy_alter_table=ON。我使用了(创建新表,移动数据,删除旧表,将新表重命名为旧表名称)的方法。 - Shakita
显示剩余15条评论
1个回答

10

TL;DR

如果以前可用的 ALTER TABLE... 在升级到 SQLite 版本 3.26 或更高版本后无法工作,则“快速修复”的方法是在进行这些更改之前使用 PRAGMA legacy_alter_table=ON。然而,最好重新组织命令(参见“The Safe Way”)。如果您正在使用 post-3.26 版本的 SQLite 开始新项目,则建议使用“The Safe Way”并避免使用 PRAGMA

What Happened

您的问题是由于 SQLite 版本3.25.0 (2018-09-15)和3.26.0 (2018-12-01)对 ALTER TABLE 命令所做的更改引起的。有关官方文档,请参见 SQLite 网站上的 ALTER TABLE

在此版本之前,重命名表(例如,从 MY_TABLEA_BETTER_NAMED_TABLE仅仅 更改了表的名称。任何对该表的引用(即在外键(FK)约束或触发器中)都没有被重命名。如果目标是仅仅重命名表,那么这可能被认为是一个 bug,因为没有(简单的、官方的)方法来更改这些引用,你将得到一个不一致的数据库。

上述更改通过将表名的更改传播到任何引用该表的地方而“解决”了此问题。因此,您可以重命名被FK约束或触发器引用的表,并保持一致的数据库。
当它引起问题时,OP的问题是间接由于与非Lite数据库引擎相比 ALTER TABLE 命令的非常有限的能力而引起的。在SQLite中,它能做的只是更改表的名称,列的名称或添加新列(到行的“末尾”)。如果您需要对表进行比这更复杂的操作,则必须变得“有创意”(基本上是创建一个新的替换表并从旧表填充它)。然而,这有两种方法,其中一种被上述更改“试图变得更加有帮助”所破坏。
安全的方法:
1. 创建一个新的(例如MY_TABLE_NEW),具有所需的所有新属性。 2. 将现有数据从MY_TABLE迁移到MY_TABLE_NEW(根据需要添加默认/丢失的值)。 3. 删除原始表MY_TABLE。此时,引用MY_TABLE的任何FK约束和触发器都将失败,但这并不重要。
  • 使用ALTER TABLE MY_TABLE_NEW RENAME TO MY_TABLE命令将新的、格式正确的表的名称更改回原始表的名称。任何外键约束/触发器引用现在都将再次满足(假设它们不是对已删除的列!)

  • 上述方法在更改前后都适用。在更改之前,不考虑外键约束/触发器;在更改之后,不会有对MY_TABLE_NEW的引用,因此不会有数据冗余。

    不完整的方法

    在更改之前,以下步骤同样有效:

    • 使用ALTER TABLE MY_TABLE RENAME TO MY_TABLE_OLD。此时,在旧版本中任何外键约束/触发器都将不一致,但这并不重要。

    • 创建一个新的替代MY_TABLE,具有您需要的所有新属性。现在任何引用都将被满足。

    • 将现有数据从MY_TABLE_OLD迁移到MY_TABLE(根据需要添加默认/缺少的值)。

    • 删除原始(但已重命名)表MY_TABLE_OLD

    问题出现在新版本中,因为它会将对原始表(MY_TABLE)的任何引用重命名,使它们现在引用即将被删除的表(MY_TABLE_OLD)。其他步骤都不会影响这些引用,因此您将得到一个不一致的数据库(引用不存在的 MY_TABLE_OLD)。
    解决方法:要么改变表操作步骤的顺序,按照安全的方式进行,或者-如ALTER TABLE页面所述-在ALTER TABLE命令之前发出PRAGMA legacy_alter_table = ON命令。这将防止“改进”的行为将对MY_TABLE的引用重命名为MY_TABLE_OLD

    如果您能在帖子前加上TL;DR,那就太好了:使用PRAGMA legacy_alter_table = ON。 - Max
    @Max 经过一番思考,我已经这样做了... 但是,在大多数情况下,我建议重新排列命令,以避免需要使用 pragma - TripeHound
    我以前已经用旧方法重命名了一个表格,花费了数小时才修复,因为我没有足够的经验首先意识到引用表也必须被替换,而不仅仅是错误复制的表格。 - Luther
    安全方式运行得非常好!谢谢!! - Alessandro

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接