我正在开发一个应用程序,它将在Oracle中读取一个非常大的数据库表。然后它将处理它并保存结果。一种方法是一次处理所有行。这需要很长时间才能运行,并且当新行添加到表中时,我无法获得这些行。
所以我正在寻找一种解决方案,我一次只能读取一些行,并在下次读取表时留下这些行并读取其他行。
任何其他避免一次性阅读整张桌子的解决方案也是受欢迎的。
答案 0 :(得分:0)
添加"标记"柱:
ALTER TABLE your_table ADD (new_flag BOOL DEFAULT FALSE);
...然后在处理行时标记行:
// job is starting
do {
db.startTransaction();
workingSet = db.select(
"SELECT primary_key, other_columns FROM your_table WHERE new_flag = FALSE LIMIT 100 FOR UPDATE"
);
if (workingSet.size != 0) {
db.query(
"UPDATE your_table SET new_flag = TRUE WHERE primary_key IN " + workingSet
);
db.commit();
doSomethingUseful(workingSet);
} else {
db.rollback();
}
} while (workingSet.size != 0);
// job's done, let's cleanup
db.query("UPDATE your_table SET new_flag = FALSE WHERE new_flag = TRUE");
OP最初使用mysql标记了她的问题,但似乎目标RDBMS是Oracle。 SQL语法对于MySQL是正确的,我不知道它是否可以像Oracle一样工作。此外,可能有更好的原生Oracle解决方案。
答案 1 :(得分:0)
有数十亿条记录,我猜/希望/建议表格是分区的。可能是某些“create_date”列,但这并不重要。
数十亿条记录也意味着您必须以超过1毫秒的速度处理一行(多),以便能够在“有限”时间内计算整个表格。
我认为记录一旦创建,就不会在流程外更新。
有2个选项。您(可能)能够一次性处理整个分区。如果可以,那么创建一个单独的表来记录分区并选择一个未处理的(不是当前创建的记录)并处理它。
如果分区太大(或表没有分区),你不可能一次性处理整个分区,那么你有几个选项,IMO可能因情况而异,哪种方法最好。
1)添加标志栏
alter table T add processed number;
并选择(批量收集限制)一些未处理的,处理它们并批量写入标志
2)使用现有列获取确定性子集(PK,create_date)并选择某些边界之间的行。在单独的配置表中处理集合并更新有关边界的信息
这两种方法的缺点是它会在后续的处理开始时反复读取一些数据库块。使用标志列,它将是多块操作。使用PK它将是单块操作,但它将被缓存。
3)具有最高实现复杂度的相当罕见的选项,但最佳性能是计算到单独的配置表rowid边界,然后处理由这些边界设置的子集。例如。
select/update T where rowid > lower_bound and rowid <= higher_bound;
“常见”解决方案的最佳情况仍然是IMO一次性处理整个分区。