postgresql: смещение + лимит становится очень медленным - PullRequest
5 голосов
/ 02 ноября 2011

У меня есть таблица tmp_drop_ids с одним столбцом, id и 3,3 миллиона записей. Я хочу перебрать таблицу, что-то делать с каждыми 200 записями. У меня есть этот код:

LIMIT = 200
for offset in xrange(0, drop_count+LIMIT, LIMIT):
    print "Making tmp table with ids %s to %s/%s" % (offset, offset+LIMIT, drop_count)
    query = """DROP TABLE IF EXISTS tmp_cur_drop_ids; CREATE TABLE tmp_cur_drop_ids AS
    SELECT id FROM tmp_drop_ids ORDER BY id OFFSET %s LIMIT %s;""" % (offset, LIMIT)
    cursor.execute(query)

Сначала это работает нормально (~ 0,15 с для генерации таблицы tmp), но иногда оно будет замедляться, например, около 300 тыс. билетов на создание этой таблицы tmp потребовалось 11-12 секунд, и снова около 400 тыс. Это в основном кажется ненадежным.

Я буду использовать эти идентификаторы в других запросах, поэтому я решил, что лучшее место для их размещения - в таблице tmp. Есть ли лучший способ перебрать результаты, как этот?

Ответы [ 2 ]

10 голосов
/ 02 ноября 2011

Вместо этого используйте курсор. Использование OFFSET и LIMIT довольно дорого - потому что pg должен выполнить запрос, обработать и пропустить строки OFFSET. OFFSET - это как «пропустить строки», это дорого.

документация курсора

Курсор позволяет выполнять итерацию по одному запросу.

BEGIN
DECLARE C CURSOR FOR SELECT * FROM big_table;
FETCH 300 FROM C; -- get 300 rows
FETCH 300 FROM C; -- get 300 rows
...
COMMIT;

Возможно, вы можете использовать серверный курсор без явного использования оператора DECLARE, просто с поддержкой в ​​ psycopg (поиск в разделе о серверных курсорах).

2 голосов
/ 02 ноября 2011

Если ваши идентификаторы проиндексированы, вы можете использовать «limit» с «>», например, в псевдокоде, подобном python:

limit=200
max_processed_id=-1
query ("create table tmp_cur_drop_ids(id int)")
while true:
  query("truncate tmp_cur_drop_ids")
  query("insert into tmp_cur_drop_ids(id)" \
        + " select id from tmp_drop_ids" \
        + " where id>%d order by id limit %d" % (max_processed_id, limit))
  max_processed_id = query("select max(id) from tmp_cur_drop_ids")
  if max_processed_id == None:
    break
  process_tmp_cur_drop_ids();
query("drop table tmp_cur_drop_ids")

Таким образом, Postgres может использовать index для вашего запроса.

...