在SQLite中启用外键约束

41

我正在使用C#与SQLite,并且有一些定义了外键的表。

现在,我知道默认情况下SQLite不执行外键约束,但我想把它们打开。

是否可以通过代码来实现这一点?我查阅了一个相关的问题,但我不确定如何通过C#代码来实现。我正在使用Visual Studio 2008中可用的最新的SQLite插件设计我的表。

conn.Open();
SQLiteCommand cmd = new SQLiteCommand("PRAGMA foreign_keys = ON", conn);
cmd.ExecuteNonQuery(); 
conn.Close();

我需要在重新打开连接时保留这个更改。这可能吗?

8个回答

72

这篇文章中最终弄明白了。PRAGMA foreign_key设置不会持久化,但是您可以在ConnectionString中每次建立连接时设置它。这样可以使用Visual Studio的表适配器。

  1. 确保您安装了最新版本(1.0.73.0)的system.data.sqlite(1.0.66.0将无法工作)。
  2. ConnectionString更改为data source=C:\Dbs\myDb.db;foreign keys=true;(将C:\ Dbs \ myDb.db替换为您的sqlite数据库)。

1
我当时使用的是版本1.0.66.0。我没有使用外键功能。很高兴知道这个问题终于得到解决。我会记住这个问题以备将来工作之需。谢谢。 - patentfox
我尝试了此帖子中提到的所有其他方法,只有这个方法适用于我。 - Dieter Menne
4
我很震惊,在2020年,SQLite仍然会禁用外键约束,即使已明确启用它们!谈论到数据严重损坏。 - Clint Pachl

6

开启Pragma:

PRAGMA foreign_keys = ON;

你可以像执行其他SQL语句一样执行它。

1
我正在使用SqliteDataAdapter,它管理连接的打开和关闭。我相信当一个连接被关闭时,这些信息就会丢失。我该如何使这些信息持久化? - patentfox
@kaustubh:你说得对,foreign_key pragma 是会话本地的。我不知道 SqliteDataAdapter 是如何工作的,但它似乎在构造函数中需要一个 SQLConnection 参数。当您创建连接时,是否可以针对该连接发出 pragma 呢? - Marcelo Cantos
似乎没有办法在连接字符串中指定这些信息。有没有一种方法可以指定每次打开连接时执行的命令? - patentfox
@kaustubh:我的意思是你应该创建一个SQLiteConnection,调用它的pragma语句,然后使用连接来构造SQLiteDataAdapter。 - Marcelo Cantos

5

我也曾经遇到过这个问题。我决定调查连接到数据库时在SQLDriverConnect()中生成的完整连接字符串。以下是它返回的内容:

'Driver={SQLite3 ODBC Driver};Database=C:\Users\Staples\Documents\SQLLiteTest.s3db;StepAPI=;SyncPragma=;NoTXN=;Timeout=;ShortNames=;LongNames=;NoCreat=;NoWCHAR=;FKSupport=;JournalMode=;OEMCP=;LoadExt=;BigInt=;PWD='

如您所见,这个属性是 FKSupport。 在我的连接字符串中添加 FKSupport=True; 后,它返回了以下内容:

'Driver={SQLite3 ODBCDriver};Database=C:\Users\Staples\Documents\SQLLiteTest.s3db;StepAPI=;SyncPragma=;NoTXN=;Timeout=;ShortNames=;LongNames=;NoCreat=;NoWCHAR=;FKSupport=True;JournalMode=;OEMCP=;LoadExt=;BigInt=;PWD='

完成!外键约束已经生效。


3
另一种解决方案是在每个查询中执行“PRAGMA foreign_keys=ON”。
    SQLiteConnection connection = new SQLiteConnection("Data Source=" + dbSQLite + ";Read Only=False;");
    connection.Open();
    SQLiteCommand mycommand = new SQLiteCommand(connection);
    mycommand.CommandText = "PRAGMA foreign_keys=ON";
    mycommand.ExecuteNonQuery();
    mycommand.CommandText = "DELETE FROM table WHERE ID=x";
    mycommand.ExecuteReader();
    connection.Close();
如果将其放入一个函数中,并传递CommandText参数,则可以重复使用它。

2
这些应该提供您正在寻找的信息:

http://www.sqlite.org/faq.html#q22

http://www.sqlite.org/foreignkeys.html#fk_enable

简而言之,版本号在3.6.19之前的SQLite数据库不会强制执行外键约束,但可以使用触发器来模拟;从版本3.6.19开始,SQLite数据库可以强制执行外键约束,但需要使用PRAGMA foreign_keys = ON语句在每个连接中启用,并且SQLite必须编译时启用触发器和外键支持(我希望这对于任何二进制分发版本都是默认情况)。

我正在使用SqliteDataAdapter,它可以自动管理连接的打开和关闭。那么,每次进行更新时,我该如何强制执行这个PRAGMA呢? - patentfox

1

在您的连接字符串中添加:";EnforceFKConstraints=Yes|True|1;"


看到你的回复,我感到非常兴奋,似乎这是完美的解决方案。但是,它并不起作用 :( 也许 .NET 的 Sqlite 数据提供程序还不支持它。如果支持,那么我已经无计可施了,不知道如何使其工作。 - patentfox
@kaustubh:尝试使用[dbFacade](http://krez0n.org.ua/wp-content/uploads/files/dbFacade.zip)。也许它支持那个?[作者的博客](http://krez0n.org.ua/archives/84)不幸是用俄语写的(他来自乌克兰,似乎如此),但我可以帮助你翻译。 - abatishchev
@kaustubh:顺便问一下,你是添加了 EnforceFKConstraints=Yes 还是 EnforceFKConstraints=True 或者 EnforceFKConstraints=1。还是这三个都加了? - abatishchev

0

看起来你可以像执行Select或Update语句一样,在你的DB连接上执行SQL命令PRAGMA foreign_keys = ON;。不过你必须确保你的SQLite是编译了外键等相关功能的版本。请参见这里


-2
In C++ store app the following code helped me to enable PRAGMA foreign_keys
sqlite3_stmt* stmt;
sqlite3_prepare(db, "PRAGMA foreign_keys = ON;", -1, &stmt, 0); 
sqlite3_step(stmt);

I called this after creating db using the call of 

int rc = sqlite3_open16(path->Data(), &db);

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