如何从没有主键的 Oracle Table 中删除大量记录
How to delete large quantity of records from Oracle Table that has no primary key
情况:我正在将整个 SQL table 加载到我的程序中。为方便起见,我使用 pandas 来维护行数据。然后我创建了一个数据框,其中包含我想从 SQL table 中删除的行。不幸的是(我不能改变这个) table 除了内置的 Oracle ROWID(不是真正的 table column its a pseudocolumn),但如果需要,我可以将 ROWID 作为数据框的一部分。
table 有数十万行,我可能会删除每个 运行 程序的几千条记录。
问题:
使用 Cx_Oracle 删除没有主键的多个 rows/records 的最佳方法是什么? 我不认为创建循环来提交数千个delete 语句非常有效或 pythonic。尽管我担心构建一个单一的 SQL delete 语句以 ROWID 键控并且包含一个包含数千个项目的子句:
Where ROWID IN ('eg1','eg2',........, 'eg2345')
这个担忧是否成立?有什么建议吗?
您可以使用 cursor.executemany() 一次删除多行。像下面这样的东西应该可以工作:
dataToDelete = [['eg1'], ['eg2'], ...., ['eg2345']]
cursor.executemany("delete from sometable where rowid = :1", dataToDelete)
使用 ROWID
既然您可以使用 ROWID
,那将是最理想的方式。并且根据 Oracle 版本,查询长度限制可能足够大,可以满足 IN
子句中包含那么多元素的查询。问题是 IN
表达式列表中的 个元素 - limited to 1000.
因此,您必须一次将 RowID 列表分成 1000 个一组,或者一次只删除一行;有或没有 executemany()
.
>>> len(delrows) # rowids to delete
5000
>>> q = 'DELETE FROM sometable WHERE ROWID IN (' + ', '.join(f"'{row}'" for row in delrows) + ')'
>>> len(q) # length of the query
55037
>>> # let's try with just the first 1000 id's and no extra spaces
... q = 'DELETE FROM sometable WHERE ROWID IN (' + ','.join(f"'{row}'" for row in delrows[:1000]) + ')'
>>> len(q)
10038
您可能在查询长度限制内,甚至可以使用最小的 ','
项目分隔符保存一些字符。
没有 ROWID
在没有主键或 ROWID 的情况下,识别每一行的唯一方法是在 WHERE 子句中指定所有列,并且一次执行多行,需要将它们一起进行“或”运算:
DELETE FROM sometable
WHERE ( col1 = 'val1'
AND col2 = 'val2'
AND col3 = 'val3' ) -- row 1
OR ( col1 = 'other2'
AND col2 = 'value2'
AND col3 = 'val3' ) -- row 2
OR ( ... ) -- etc
如您所见,这不是构建的最佳查询,但允许您在没有 ROWID 的情况下执行此操作。
在这两种情况下,您可能不需要使用参数化查询,因为 1 中的 IN
列表或 2 中的 OR
分组是可变的。 (是的,您可以在使用数千个参数构建整个扩展 SQL 之后创建它的参数化。不确定它的限制是什么。) executemany()
方式绝对更容易编写和执行,但速度,单个大型查询(以上两个中的任何一个)可能会胜过具有数千个项目的 executemany。
情况:我正在将整个 SQL table 加载到我的程序中。为方便起见,我使用 pandas 来维护行数据。然后我创建了一个数据框,其中包含我想从 SQL table 中删除的行。不幸的是(我不能改变这个) table 除了内置的 Oracle ROWID(不是真正的 table column its a pseudocolumn),但如果需要,我可以将 ROWID 作为数据框的一部分。
table 有数十万行,我可能会删除每个 运行 程序的几千条记录。
问题: 使用 Cx_Oracle 删除没有主键的多个 rows/records 的最佳方法是什么? 我不认为创建循环来提交数千个delete 语句非常有效或 pythonic。尽管我担心构建一个单一的 SQL delete 语句以 ROWID 键控并且包含一个包含数千个项目的子句:
Where ROWID IN ('eg1','eg2',........, 'eg2345')
这个担忧是否成立?有什么建议吗?
您可以使用 cursor.executemany() 一次删除多行。像下面这样的东西应该可以工作:
dataToDelete = [['eg1'], ['eg2'], ...., ['eg2345']]
cursor.executemany("delete from sometable where rowid = :1", dataToDelete)
使用 ROWID
既然您可以使用 ROWID
,那将是最理想的方式。并且根据 Oracle 版本,查询长度限制可能足够大,可以满足 IN
子句中包含那么多元素的查询。问题是 IN
表达式列表中的 个元素 - limited to 1000.
因此,您必须一次将 RowID 列表分成 1000 个一组,或者一次只删除一行;有或没有 executemany()
.
>>> len(delrows) # rowids to delete
5000
>>> q = 'DELETE FROM sometable WHERE ROWID IN (' + ', '.join(f"'{row}'" for row in delrows) + ')'
>>> len(q) # length of the query
55037
>>> # let's try with just the first 1000 id's and no extra spaces
... q = 'DELETE FROM sometable WHERE ROWID IN (' + ','.join(f"'{row}'" for row in delrows[:1000]) + ')'
>>> len(q)
10038
您可能在查询长度限制内,甚至可以使用最小的 ','
项目分隔符保存一些字符。
没有 ROWID
在没有主键或 ROWID 的情况下,识别每一行的唯一方法是在 WHERE 子句中指定所有列,并且一次执行多行,需要将它们一起进行“或”运算:
DELETE FROM sometable
WHERE ( col1 = 'val1'
AND col2 = 'val2'
AND col3 = 'val3' ) -- row 1
OR ( col1 = 'other2'
AND col2 = 'value2'
AND col3 = 'val3' ) -- row 2
OR ( ... ) -- etc
如您所见,这不是构建的最佳查询,但允许您在没有 ROWID 的情况下执行此操作。
在这两种情况下,您可能不需要使用参数化查询,因为 1 中的 IN
列表或 2 中的 OR
分组是可变的。 (是的,您可以在使用数千个参数构建整个扩展 SQL 之后创建它的参数化。不确定它的限制是什么。) executemany()
方式绝对更容易编写和执行,但速度,单个大型查询(以上两个中的任何一个)可能会胜过具有数千个项目的 executemany。