如何以原子方式运行 2 个 SQL“SELECT”?或者在我处理它们之前获取行数的任何其他更好的方法
Posted
技术标签:
【中文标题】如何以原子方式运行 2 个 SQL“SELECT”?或者在我处理它们之前获取行数的任何其他更好的方法【英文标题】:How to run 2 SQL "SELECT"s atomically? Or any other better way to get number of rows before i process them 【发布时间】:2015-08-02 07:04:27 【问题描述】:我正在尝试以原子方式运行
ResultSet resSet;
resSet = statement.executeQuery("SELECT COUNT(*) FROM table");
resSet.next()
long rowCount = resSet.getLong(1);
resSet = statement.executeQuery("SELECT * FROM table");
// read data of known row count...
我的问题是最好的方法是什么?
目前我发现我可以做到:
connection.setAutoCommit(false);
connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE)
// call 2 SQL queries above
connection.commit();
这种方法似乎行得通。我测试了另一个线程被阻止在第一个SELECT
和commit()
之间执行INSERT
。
这是正确和最佳的方式吗?我可以确定这样我的 COUNT 将始终与从下一个选择返回的行数相同吗?
我也希望Connection.TRANSACTION_SERIALIZABLE
代替Connection.TRANSACTION_REPEATABLE_READ
就足够了。
但它在 Derby 10.11.1.1 中不起作用。它是一个错误吗?我是数据库业务的新手,但它对 H2 数据库按预期工作 - 因此我预计它可能是一个 derby 错误......
请注意,我已经知道您可以在哪里执行的解决方案:
statement = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
resultSet = statement.executeQuery("SELECT * FROM table");
if (ResultSet.last())
int rowCount = ResultSet.getRow();
ResultSet.beforeFirst();
while(ResultSet.next())...
但是这个解决方案并不是最优的。对于德比,我测量它慢了约 7 倍。对于 H2,它慢了约 2 倍。
【问题讨论】:
为什么不简单地执行第二个查询,并为每个读取行增加一个计数器。 还注意到有趣的事情,当connection.commit()
在executeQuery()
之后resultSet.next()
之前,H2 数据库将正确打印结果。但是 Derby 数据库会为您提供修改后的数据。因此,在 Derby 中,要使其以原子方式工作,您需要在从 resultSet
读出数据后输入 commit()
坦率地说,我不会太在意这种用例的隔离。假设您很不幸,并且在计数查询和第二个查询之间添加了一行。所以呢?处理第 n-1 个元素时,您的进度条将显示为 100% 而不是 99.999%。这真的有问题吗?
@VitBernatik 您可以在定义 url 连接时使用所有应用程序的这些参数初始化连接,因此根本不需要调用 Connection#setAutocommit
或 Connection#setTransactionIsolation
,所有连接都将被创建像这样。此外,最好只调用第二个查询并使用计数器变量来检查您获得了多少行,而不是执行(同样繁重的)查询两次。
高度依赖于数据库(版本、设置、驱动程序),序列化工作的好坏和效率低下。我会不惜一切代价避免它。
【参考方案1】:
怎么样
SELECT (SELECT COUNT(*) FROM table) c, * FROM table
【讨论】:
哇,谢谢。但我不确定它是否不是太大的开销。想象一下,我有 1M 行,每行我都会复制这个计数,而我只需要计数一次。 同样适用于H2,但是Derby不理解这样的SQL语句。它抱怨第二个星号java.sql.SQLSyntaxErrorException: Syntax error: Encountered "*" at line 1, column 43.
您应该枚举所有列名而不是“*”。内部 SELECT 是外部 SELECT 中的一个常量值,所以我认为它会被评估一次。但这取决于实施。
是的,当我明确列出像 SELECT (SELECT COUNT(*) FROM table) c, ID, VAR1 FROM table
这样的列时,它也适用于 Derby DB。
所以我确实测试了这种方法,它看起来很有趣。它拓宽了我的 SQL 知识。但它并不快。我对 200K 行(4 列)的测试。表明 2 次连续读取比嵌入式数据库的组合选择更快。在 Derby 的情况下,我节省了 15% 的时间(~109 毫秒)。在 H2 的情况下,我节省了 35% 的时间(652 毫秒)。 Derby 的总读取时间(对于更快的连续读取版本)为 777 毫秒,H2 数据库为 1839 毫秒。 (5 次测量的平均值)以上是关于如何以原子方式运行 2 个 SQL“SELECT”?或者在我处理它们之前获取行数的任何其他更好的方法的主要内容,如果未能解决你的问题,请参考以下文章
EF4 Code First + SQL Server CE:以原子方式保存双向引用
在 SQL Server 中,如何以类似于 Oracle 的“SELECT FOR UPDATE WAIT”的方式锁定单行?