C# 操作SQLite,如何在开启insert事务后还能执行不用事务的select语句?
本帖最后由 getstr88 于 2022-7-5 10:13 编辑客户的需求是:
有一批username,要合并到新的数据库中
要求,插入时为了速度,要用事务,每100条数据commit一次。但每插入一条时,要先判断,如果新数据库中有这个username了,那本程序停止掉。
所以,我就开了个事务用于insert,本想每从旧数据库读一个数据,然后用非事务的sqliteCommand在新数据库中select,如果没有,就向事务中加一个insert,如果已经有了,就把之前事务调教掉,然后停止程序
但实测发现,已经开启事务后,就不允许非事务的sqliteCommand执行,会抛出异常
然后我给客户提了几个方案,都不被允许。。那么我还有什么其他办法?
我被否决的方案:
1、开2个sqliteConnection,一个开启事务进行inset,另一个执行普通select
2、先select验证一批不存在于新数据库,然后再用事务insert。然后再select验证一批不存在于新数据库,再用事务insert……
这个也被客户否决了,说,验1条插一条,不能先select一批在做
所以,想不出还有其他什么办法,满足客户这2点要求了
另外,3楼我还有一个问题(附代码),也请大佬们解答下 第2完全没看懂。。。。 另外,还要请教,虽然方案1直接被客户否决了
因为我之前没用过SQLite,还是想测试下,是否支持多个Connection
本以为下面测试代码应该行得通,结果报错为:SQLite Error 5: 'database is locked
(可以确保其他程序没有使用的)
也请教下这样的原因:
using Microsoft.Data.Sqlite;
namespace TestDB
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
SqliteConnection connectionForSelect = new SqliteConnection(@"DataSource=D:\111.db");
connectionForSelect.Open();
SqliteConnection connectionForTransaction = new SqliteConnection(@"DataSource=D:\111.db");
connectionForTransaction.Open();
/**
* 新建名为my_test_table的表格,它有一列为id,然后插入一行数据3
*/
SqliteCommand createTableCmd = connectionForSelect.CreateCommand();
createTableCmd.CommandText = "DROP TABLE IF EXISTS 'my_test_table'";
createTableCmd.ExecuteNonQuery();
createTableCmd.CommandText = "CREATE TABLE my_test_table(id integer primary key)";
createTableCmd.ExecuteNonQuery();
createTableCmd.CommandText = "INSERT INTO my_test_table(id) values(3)";
createTableCmd.ExecuteNonQuery();
SqliteCommand selectDataCmd = connectionForSelect.CreateCommand();
using (SqliteTransaction transaction = connectionForTransaction.BeginTransaction())
{
int currentId = 0;
while (true)
{
selectDataCmd.CommandText = $"SELECT * from my_test_table where id={currentId}";
SqliteDataReader reader = selectDataCmd.ExecuteReader();
if (reader.HasRows == true)
{
// 遇到已存在的,则停止,并提交事务
transaction.Commit();
reader.Close();
break;
}
else
{
// 没有则插入
SqliteCommand insertCmd = connectionForTransaction.CreateCommand();
insertCmd.Transaction = transaction;
insertCmd.CommandText = $"INSERT INTO my_test_table(id) values({currentId})";
insertCmd.ExecuteNonQuery();
}
reader.Close();
currentId++;
}
}
connectionForSelect.Close();
connectionForTransaction.Close();
SqliteConnection.ClearAllPools();
MessageBox.Show("完毕");
}
}
}
dlxg 发表于 2022-7-5 08:28
第2完全没看懂。。。。
就是说,既然不允许开启事务后再执行不用事务的SQL
那我就先从旧数据,比如取出50个数据,然后在新数据库select 50次,验证是不是已经有了
如果没有,则开启事务,将这50个用事务批量insert。然后关闭事务
就保证在事务开启时,不会select
但是客户不允许我这样“绕路”的方法 首先,我只会简单的sql操作,不会复杂的。
其次,提供个思路,不知道有没有用。
客户说的,每100条数据,commit一次,但每插入一条,要先判断是否存在。
这样的话,试着搞三个集合,集合A(要合并到数据库的username),集合B(确定查重后的username),集合C(已经存在的username),搞一个循环,判断集合A中的username是否在数据库中存在,如果存在,将该username放入集合C,如果不存在,将该username放入集合B,循环结束后,集合A应该=集合B+集合C。
然后循环写入集合B到数据库。
不知道这样是不是你需要的?
至于你贴出来的代码,你不能对同一个数据库打开两次,也就是在第一次打开后没有关闭的情况下,就打开第二次。
解决方法:可以尝试用进程。 dlxg 发表于 2022-7-5 08:49
首先,我只会简单的sql操作,不会复杂的。
其次,提供个思路,不知道有没有用。
客户说的,每100条数据, ...
额。首先客户要求查一条验1条,你都搞集合了,难道不是select一批了?客户不允许
另外,不能对同一个数据库打开两次。really??? SQLite 我查了下,支持多个客户端同时连接,并发进行读。只是不能同时写
而我开两个connect,一个只select、另一个用事务写。这样也不行? dlxg 发表于 2022-7-5 08:49
首先,我只会简单的sql操作,不会复杂的。
其次,提供个思路,不知道有没有用。
客户说的,每100条数据, ...
我刚才试下。可以一个程序开多个Connection ,只要不是同时写就行。逐个用不同connection写是没有问题的
那么,是不是说sqltite开启事务后,也就不允许其他connection进行读了?但感觉没必要啊。其他的读又有什么影响呢 插入时为了速度,要用事务,每100条数据commit一次。但每插入一条时,要先判断,如果新数据库中有这个username了,那本程序停止掉。
插入速度和事务有毛关系,奇葩需求 加锁吗,一次只允许一个请求访问,效率低了点 fightingmy 发表于 2022-7-5 09:21
插入时为了速度,要用事务,每100条数据commit一次。但每插入一条时,要先判断,如果新数据库中有这个usern ...
????
这个我挺客户的。你可以试一下。插入1万条数据。用事务我这里0.02秒,不用事务25秒。多少倍的差距。。