mod() для нарезки на батчи
В бытность работы в Вайлдберриз, часто приходилось реализовывать загрузку таблицы частями - мой airflow-оператор успешно работал на кусках примерно по 100 тыс. - 1 млн. строк, в зависимости от ширины таблицы. Проблем с нарезкой не было - почти всегда было какое то непрерывно растущее поле - обычно целочисленное, условный row_id. Нашёл минимальное/максимальное значение и делаешь выборку
...
for cycle in range(min_id, max_id, chunk_size):
query = f"select * from table where row_id between {cycle} and {cycle + chunk_size - 1}"
cur.execute(query)
...
или более тяжёлый для сервера вариант (сортировка), зато батчи получаются ровные, независимо от пропусков в row_id
...
while cycle < max_id:
query = f"select * from table where row_id > {cycle} order by row_id limit {chunk_size}"
cur.execute(query)
# cycle = row_id из последней строки в батче
...
Если целочисленного поля нет, можно обойтись датой или таймстампом (условно create_dt/create_dttm) по аналогии
Неожиданно вспомнилось про это, когда надо было сделать джойн огромных таблиц и запрос никак не укладывался в лимит по спиллам. Нарезать по первому варианту - муторно, в id много пропусков. По второму варианту - сортировать огроменную таблицу, да ещё несколько раз - тоже не хочется (по идее тоже убьют запрос по спиллам). Подсмотрел у коллеги такой вариант:
const = 2 # количество батчей на которые разбиваем запрос
i = 1
while i <= const:
print(f'батч {i} из {const}')
query = f'''
insert into dst_table
select *
from t1
join t2 on t1.id = t2.id
where mod(t1.serno, {const}) = {i}'''
cur.execute(query)
i = i + 1
...
“Углехак” (ugly hack) - как говорил мой тимлид в Вайлдберриз, но удобно для одноразовых скриптов