Ускорение тысяч запросов SELECT

Ситуация

  • Работа с Python 3.7.2
  • Я прочитал преимущество таблицы MariaDB с 5M строками на сервере.
  • У меня есть локальный текстовый файл с целыми числами 7K, по одному в строке.
  • Целые числа представляют IDX таблицы.
  • Столбец IDX таблицы является первичным ключом. (так что я полагаю, он автоматически индексируется?)

Проблема

Мне нужно выбрать все строки, IDX которых находится в текстовом файле.

Мои усилия

Версия 1

Сделайте 7K запросов, по одному на каждую строку в текстовом файле. Это составляет примерно 130 запросов в секунду, выполнение которых занимает около 1 минуты.

import pymysql
connection = pymysql.connect(....)
with connection.cursor() as cursor:
    query = (
        "SELECT *"
        " FROM TABLE1"
        " WHERE IDX = %(idx)s;"
    )

    all_selected = {}
    with open("idx_list.txt", "r") as f:
        for idx in f:
            idx = idx.strip()
            if idx:
                idx = int(idx)
                parameters = {"idx": idx}
                cursor.execute(query, parameters)
                result = cursor.fetchall()[0]
                all_selected[idx] = result

Версия 2

Выберите всю таблицу, выполните итерацию по строкам курсора и выбора вишни. Цикл for над .fetchall_unbuffered() обрабатывает 30-40 тыс. строк в секунду, а выполнение всего скрипта занимает около 3 минут.

import pymysql
connection = pymysql.connect(....)
with connection.cursor() as cursor:
    query = "SELECT * FROM TABLE1"

    set_of_idx = set()
    with open("idx_list.txt", "r") as f:
        for line in f:
            if line.strip():
                line = int(line.strip())
                set_of_idx.add(line)


    all_selected = {}
    cursor.execute(query)
    for row in cursor.fetchall_unbuffered():
        if row[0] in set_of_idx:
            all_selected[row[0]] = row[1:]

Ожидаемое поведение

Мне нужно выбирать быстрее, потому что количество IDX в текстовом файле в будущем вырастет до 10-100 тысяч.

Я проконсультировался с другими ответами, включая это, но я не могу использовать из этого, так как я только прочитал previlege, поэтому невозможно создать другую таблицу для присоединения.

Итак, как я могу сделать выбор быстрее?


person Ignatius    schedule 27.02.2019    source источник
comment
Почему бы не загрузить текстовый файл в таблицу?   -  person Strawberry    schedule 27.02.2019
comment
@Strawberry Возможно ли это с доступом только для чтения к базе данных?   -  person Ignatius    schedule 27.02.2019
comment
Можно ли получить доступ к CREATE TEMPORARY TABLE?   -  person danblack    schedule 27.02.2019
comment
@danblack Мне придется поговорить с менеджером БД, и если это так, я понимаю, что смогу JOIN две таблицы, вероятно, решив проблему. Если нет, то у меня мало шансов?   -  person Ignatius    schedule 27.02.2019
comment
Покажите менеджеру БД свой альтернативный код. Этого должно быть достаточно, чтобы предоставить вам требуемый CREATE TEMPORARY TABLE доступ. :-)   -  person danblack    schedule 27.02.2019


Ответы (2)


Реализация временной таблицы будет выглядеть так:

connection = pymysql.connect(....,local_infile=True)
with connection.cursor() as cursor:
    cursor.execute("CREATE TEMPORARY TABLE R (IDX INT PRIMARY KEY)")
    cursor.execute("LOAD DATA LOCAL INFILE 'idx_list.txt' INTO R")
    cursor.execute("SELECT TABLE1.* FROM TABLE1 JOIN R USING IDX")
    ..
    cursor.execute("DROP TEMPORARY TABLE R")
person danblack    schedule 27.02.2019
comment
Я работал над этим (просматривая документацию для загрузки данных), когда переполнение стека уведомило меня о вашем ответе... и спасибо! Мне пришлось бы потратить более получаса, чтобы охватить всю необходимую документацию. - person Ignatius; 27.02.2019
comment
@Taegyung имейте в виду, что таблица памяти может быть преобразована в работу на диске, а не в памяти, но об этом должны знать администраторы баз данных. - person Raymond Nijland; 27.02.2019

Благодаря подсказке (или больше, чем подсказке) от @danblack я смог добиться желаемого результата с помощью следующего запроса.

query = (
    "SELECT *"
    " FROM TABLE1"
    " INNER JOIN R"
    " ON R.IDX = TABLE1.IDX;"
)
cursor.execute(query)

У меня не сработало выражение SELECT от danblack, вызвав ошибку:

pymysql.err.ProgrammingError: (1064, «У вас есть ошибка в синтаксисе SQL; проверьте руководство, соответствующее вашей версии сервера MariaDB, для правильного синтаксиса для использования рядом с «IDX» в строке 1»)

Вероятно, это связано с синтаксисом соединения MariaDB, поэтому я проконсультировался с Документация MariaDB по объединению таблиц.

И теперь он выбирает 7 тыс. строк за 0,9 секунды.

Оставив здесь ответ только для полноты и для будущих читателей.

person Ignatius    schedule 28.02.2019