所以我有一个大型数据库,我无法立即在内存中保存。我必须循环遍历表中的每个项目,处理它,并将处理后的数据放入表格中的另一列。
当我循环我的光标时,如果我尝试运行更新语句,它会截断记录集(我相信因为它重新定位了游标对象)。
问题:
创建第二个游标对象以运行更新语句是否允许我继续循环原始的select语句?
我是否需要第二次连接数据库才能拥有第二个游标对象,这样我才能做到这一点?
sqlite如何响应与数据库的两个连接,一个从表中读取,另一个写入它?
我的代码(简化):
import sqlite3
class DataManager():
""" Manages database (used below).
I cut this class way down to avoid confusion in the question.
"""
def __init__(self, db_path):
self.connection = sqlite3.connect(db_path)
self.connection.text_factory = str
self.cursor = self.connection.cursor()
def genRecordset(self, str_sql, subs=tuple()):
""" Generate records as tuples, for str_sql.
"""
self.cursor.execute(str_sql, subs)
for row in self.cursor:
yield row
select = """
SELECT id, unprocessed_content
FROM data_table
WHERE processed_content IS NULL
"""
update = """
UPDATE data_table
SET processed_content = ?
WHERE id = ?
"""
data_manager = DataManager(r'C:\myDatabase.db')
subs = []
for row in data_manager.genRecordset(str_sql):
id, unprocessed_content = row
processed_content = processContent(unprocessed_content)
subs.append((processed_content, id))
#every n records update the database (whenever I run out of memory)
if len(subs) >= 1000:
data_manager.cursor.executemany(update, subs)
data_manager.connection.commit()
subs = []
#update remaining records
if subs:
data_manager.cursor.executemany(update, subs)
data_manager.connection.commit()
我尝试的另一种方法是将我的select语句修改为:
select = """
SELECT id, unprocessed_content
FROM data_table
WHERE processed_content IS NULL
LIMIT 1000
"""
然后我会这样做:
recordset = data_manager.cursor.execute(select)
while recordset:
#do update stuff...
recordset = data_manager.cursor.execute(select)
我遇到的问题是我的真正的选择语句中有一个JOIN并且需要一段时间,因此多次执行JOIN非常耗费时间。我试图通过仅执行一次选择来加速该过程,然后使用生成器,因此我不必将其全部保存在内存中。
解决方案:
好的,所以前两个问题的答案是“不”。对于我的第三个问题,一旦与数据库建立连接,它就会锁定整个数据库,因此在第一个连接关闭之前,另一个连接将无法执行任何操作。
我找不到它的源代码,但是根据经验证据我认为连接一次只能使用一个游标对象,而最后一次运行查询优先。这意味着,当我循环选择的记录集时,一次产生一行,一旦我运行第一个更新语句,我的生成器就会停止产生行。
我的解决方案是创建一个临时数据库,我使用id粘贴processed_content,这样我每个数据库都有一个连接/游标对象,并且可以继续循环选定的记录集,同时定期插入临时数据库。一旦我到达所选记录集的末尾,我将临时数据库中的数据传回原始数据库。
如果有人确切知道连接/光标对象,请在评论中告诉我。
答案 0 :(得分:3)
我认为你有大致正确的架构 - 用“游标”来表示它会混淆“旧的SQL手”,因为他们会考虑与DECLARE foo CURSOR
,{{{{}}相关的许多问题。 1}},FETCH FROM CURSOR
和其他与 SQL 游标有关的beauts。 Python DB API的“游标”只是打包和执行SQL语句的一种简便方法,不必然与 SQL 游标连接 - 它不会遇到任何这些问题 - 虽然它可能会呈现它(完全原创的)自己的;-)但是,通过你正在做的结果的“批处理”,你正确的提交等,你已经预防性地解决了我所遇到的大部分“原始问题”心。
在其他一些引擎上,我建议首先选择一个临时表,然后在更新主表时从该临时表中读取,但我不确定性能如何在sqlite中受影响,具体取决于索引你有(如果没有索引受你的更新影响,那么我怀疑这样的临时表在sqlite中根本就不是优化 - 但是我不能对你的数据运行基准测试,这是检查性能假设的唯一真正方法)。
所以,我会说,去吧! - )
答案 1 :(得分:2)
是否可以创建一个处理内容的数据库功能?如果是这样,您应该能够编写单个更新语句并让数据库完成所有工作。例如;
Update data_table
set processed_col = Process_Column(col_to_be_processed)
答案 2 :(得分:1)
由于众多原因,游标很糟糕。
我建议你使用单个UPDATE语句而不是去CURSOR路径(并且很多其他人肯定会说这个)。
您的Processed_Content可以作为参数发送到执行基于集合操作的单个查询,如下所示:
UPDATE data_table
SET processed_content = ?
WHERE processed_content IS NULL
LIMIT 1000
根据回复进行编辑:
由于每行都有Processed_Content的唯一值,因此除了使用记录集和循环之外别无选择。我过去曾多次这样做过。你的建议应该有效。