tgr*_*ray 12 python database sqlite cursor
所以我有一个大型数据库,我无法立即在内存中保存.我必须遍历表中的每个项目,处理它,并将处理后的数据放入表中的另一列.
当我循环我的光标时,如果我尝试运行更新语句,它会截断记录集(我相信因为它重新定位了游标对象).
问题:
创建第二个游标对象以运行更新语句是否允许我继续循环原始的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()
Run Code Online (Sandbox Code Playgroud)
我尝试的另一种方法是将我的select语句修改为:
select = """
SELECT id, unprocessed_content
FROM data_table
WHERE processed_content IS NULL
LIMIT 1000
"""
Run Code Online (Sandbox Code Playgroud)
然后我会这样做:
recordset = data_manager.cursor.execute(select)
while recordset:
#do update stuff...
recordset = data_manager.cursor.execute(select)
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是我的真正的 select语句中有一个JOIN并且需要一段时间,因此多次执行JOIN非常耗费时间.我试图通过仅执行一次选择来加速该过程,然后使用生成器,因此我不必将其全部保存在内存中.
解:
好的,所以前两个问题的答案是"不".对于我的第三个问题,一旦与数据库建立连接,它就会锁定整个数据库,因此在第一个连接关闭之前,另一个连接将无法执行任何操作.
我找不到它的源代码,但从经验证据来看,我认为连接一次只能使用一个游标对象,最后一次运行查询优先.这意味着,当我循环选择的记录集时,一次产生一行,一旦我运行第一个更新语句,我的生成器就会停止产生行.
我的解决方案是创建一个临时数据库,我使用id粘贴processed_content,这样我每个数据库都有一个连接/游标对象,并且可以继续循环选定的记录集,同时定期插入临时数据库.一旦我到达所选记录集的末尾,我将临时数据库中的数据传回原始数据库.
如果有人确切知道连接/光标对象,请在评论中告诉我.
我认为你有大致正确的架构 - 以“游标”的形式呈现它会让“老 SQL 手”感到困惑,因为他们会考虑与 、 、 和其他此类美感有关DECLARE foo CURSOR
的FETCH FROM CURSOR
许多WHERE CURRENT OF CURSOR
问题SQL游标。Python DB API 的“游标”只是一种打包和执行 SQL 语句的便捷方式,不一定与SQL游标相关——它不会遇到任何这些问题——尽管它可能会出现(完全原创的)自己的问题; -)但是,通过你正在做的结果的“批处理”、你正确的承诺等,你已经预防性地解决了我想到的大部分“原始问题”。
在其他一些引擎上,我建议首先对临时表进行选择,然后在更新主表时从该临时表中读取数据,但我不确定 sqlite 中的性能将如何受到影响,具体取决于您拥有的索引(如果没有索引受到您的更新的影响,那么我怀疑这样的临时表在 sqlite 中根本不是优化 - 但我无法对您的数据运行基准测试,这是检查性能假设的唯一真正方法)。
所以,我想说,加油!-)
归档时间: |
|
查看次数: |
7261 次 |
最近记录: |