我有一个INSERT
查询,它从SELECT
语句中获取值。但是由于SELECT
返回数百万条记录,因此它在MySQL服务器上增加了太多的负载。因此,我们决定将SELECT
查询分为多个部分,并通过使用LIMIT
子句执行。
INSERT INTO target_table
SELECT * FROM source_table
WHERE my_condition = value
...
LIMIT <start>, <end>
我们将继续增加起始值和结束值,直到SELECT
返回0
行为止。我也正在考虑使它成为多线程。
我该如何使用PyMySQL?
我是否需要执行SELECT
,获取结果然后生成INSERT
?
答案 0 :(得分:1)
首先,回答您的问题:在PyMySQL中,您可以通过cursor.execute
获得该值:
execute(query, args=None) Execute a query Parameters: query (str) – Query to execute. args (tuple, list or dict) – parameters used with query. (optional) Returns: Number of affected rows
因此您可以重复执行查询,直到得到的值小于所选范围。
无论如何,请考虑:
select
(假设它不像示例中那样简单),例如通过添加索引。您可能还想测试一下选择和实际插入之间的区别,以大致了解哪个部分更相关。offset
,这不太可能,请参阅下文。)否则,通常需要添加一些小暂停连续运行的(足够小的)零件之间,以将相同的工作量分散在更长的时间上。 limit
仅在拥有order by
(可能是通过主键)的情况下才有意义,否则,在连续运行中,第m
行可以与以前不同(因为顺序不固定)。这可能会或可能不会增加负载(和资源需求),具体取决于您的索引和您的where
条件。 my_condition
的值)一样,所有连续的偏移量都会移动,并且您可能会跳过一行或两次获得一行。您可能需要锁定行,这可能会阻止并行运行查询(因为它们锁定相同的行),并且还可能影响是否可以拆分事务的决定(请参见第二个要点)。offset
要求MySQL首先查找然后跳过行。因此,如果您将查询分为n
个部分,则第一行将需要处理n
次(而最后一行通常需要处理一次),因此(用于选择)的总工作量将增加{ {1}}。因此,特别是如果选择行是最相关的部分(请参阅第一个要点),这实际上会使您的情况更糟:仅最后一次运行将需要查找与当前查询相同的行数(尽管它会抛出大部分它们消失),甚至可能需要更多资源,具体取决于(n^2-n)/2
的效果。 通过使用条件中的主键,您也许可以解决一些order by
问题,例如包含这样的循环:
offset
如果select max(id) as new_max from
where id > last_id and <your condition>
order by id limit 1000 -- no offset!
是new_max
,则退出循环,否则执行插入操作:
null
然后设置insert ... select ...
where id > last_id and id <= new_max and <your condition>
并继续循环。
它将查询数量加倍,与last_id = new_max
和limit
相比,您需要知道实际的offset
。它仍然需要您的主键和您的id
-条件兼容(因此您可能需要添加适合的索引)。无论如何,如果您的搜索条件找到了源表的很大一部分(大约超过15%或20%),那么使用主键仍然是最佳的执行计划。
如果要对此进行并行化(取决于您的交易要求以及是否值得,请参见上文),您可以首先获取主键(where
)的最大值,并为每个线程分配一个范围跟...共事。例如。对于select max(id) as max_id from ...
和3个线程,以max_id=3000
中的一个开始,并将其包含在第一个查询中:
(0..1000), (1001, 2000), (2001..3000)
如果这些范围的大小相等,则可能取决于您的数据分布(并且您可能会在情况中找到更好的范围;尽管要计算确切的范围仍需要执行查询,所以您可能并不准确)。 / p>