预测下一个自动插入的行ID(SQLite)

时间:2020-03-06 14:28:34  来源:igfitidea点击:

我试图找到是否有可靠的方法(使用SQLite)在插入之前找到要插入的下一行的ID。我需要在另一个插入语句中使用id,但是没有选择立即插入并获取下一行的选项。

预测下一个ID是否像获取最后一个ID并添加一个ID一样简单?那是保证吗?

编辑:更多的推理...
我无法立即插入,因为插入可能最终会被用户取消。用户将进行一些更改,将存储SQL语句,然后用户可以保存(立即插入所有行)或者取消(不更改任何内容)。在程序崩溃的情况下,所需的功能是什么都不会改变。

解决方案

我认为这是无法完成的,因为无法确保在询问和插入之间不会插入任何内容。 (我们也许可以将表锁定为插入,但是很遗憾)

顺便说一句,我只用过MySQL,但我认为这不会有任何不同)

我们最有可能应该能够为最新ID +1. 我将查看排序表中的所有现有ID(回溯一会儿)..它们是否一致,并且每一行的ID都比最后一行多?如果是这样,我们可能会没事的。但是,我会在代码中留下注释以解释该假设。进行锁定将有助于确保我们在执行此操作时不会再获得其他行。

选择last_insert_rowid()值。

在某些情况下,例如,使用相同的数据库连接并且没有其他并发写入器,我们可能会在sqlite3_last_insert_rowid返回的值上加1而逃脱。当然,我们可以参考sqlite源代码来支持这些假设。

但是,我们也可能会认真考虑使用不需要预测下一个ID的其他方法。即使我们所使用的sqlite版本正确无误,将来情况也可能会发生变化,这无疑会使迁移到其他数据库更加困难。

插入带有某种INVALID标志的行,获取ID,根据需要对其进行编辑,必要时将其删除或者将其标记为有效。那并且不用担心序列之间的差距

顺便说一句,我们将需要弄清楚如何自己做无效的部分。根据具体情况,将某些内容标记为NULL可能有效。

编辑:如果可以,请使用伊芙的建议使用正确的交易。减少了很多工作。

一次完全擦除或者提交一系列数据库操作正是事务的目的。在用户开始摆弄之前,先查询" BEGIN;",完成后再查询" COMMIT;"。我们可以确保所有更改都已应用(如果我们提交),或者所有内容都被废弃(如果我们查询" ROLLBACK;",如果程序崩溃,断电等)。从数据库中读取数据后,还可以确保在事务结束之前数据是良好的,因此我们可以获取" MAX(id)"或者任何我们想要的内容,而不必担心竞争条件。

http://www.sqlite.org/lang_transaction.html

该主题中需要说的大多数内容都已经...但是,在执行此操作时要特别注意比赛条件。如果两个人都打开应用程序/网页/任何东西,其中一个添加一行,另一个用户将尝试插入具有相同ID的行,我们将会遇到很多问题。

我意识到使用SQLite的应用程序很小,并且SQLite具有自己的语义。此处发布的其他解决方案很可能会在此特定设置中产生我们想要的效果,但是在我看来,到目前为止,我阅读的每个解决方案从根本上都是错误的,应避免使用。

在正常环境下,应不惜一切代价避免进行用户输入交易。如果需要存储中间数据,处理此问题的方法是为此目的将信息写入暂存表,然后尝试在原子事务中写入所有信息。在多用户环境中,暂挂事务会导致死锁和并发噩梦。

在大多数环境中,我们不能假定在事务中通过SELECT检索的数据是可重复的。例如

SELECT Balance FROM Bank ...
UPDATE Bank SET Balance = valuefromselect + 1.00 WHERE ...

UPDATE之后,余额的值可能会发生变化。有时,我们可以通过在交易中首先更新我们对银行感兴趣的行来解决此问题,因为这可以确保锁定行,从而防止进一步的更新更改其值,直到交易完成。

但是,有时在这种情况下确保一致性的更好的方法是,检查更新的WHERE子句中有关数据内容的假设,并检查应用程序中的行数。在上面的示例中,当我们" UPDATE Bank"时,WHERE子句应提供预期的余额当前值:

WHERE Balance = valuefromselect

如果期望的余额不再匹配,则WHERE条件-UPDATE也不执行任何操作,并且rowcount返回0。这表明我们存在并发问题,并且当其他原因不尝试更改数据时,我们需要重新运行该操作。同时。