Оптимизация Python SQL цикл результатов - PullRequest
0 голосов
/ 04 мая 2020

У меня есть следующий рабочий код:

async def loadnames(self, ctx):
    """
    Load the current alias of every member in the database.
    Useful after joining a new guild or after a long downtime.
    """
    async with ctx.message.channel.typing():
        message = await ctx.send(f'Adding members to the database... {ctx.author.mention}')
        db = await aiomysql.connect(host=globals.mysql_host,user=globals.mysql_user,password=globals.mysql_passwd,db=globals.mysql_db)
        cur = await db.cursor()

        #await cur.execute("SELECT discord,name FROM aliases WHERE discord=%s AND name=%s", (member.id,str(member)))
        #result = await cur.fetchall()

        count = 0

        for member in ctx.guild.members: 
            try:           
                result = await cur.execute("SELECT * FROM aliases WHERE discord=%s AND name=%s", (member.id,str(member)))
                if result == 0:
                    count = count + 1
                    await cur.execute("INSERT INTO aliases (discord,name) VALUES(%s,%s)", (member.id,str(member)))
                    await db.commit()
            except:
                await ctx.send(f'Error adding `{member}`! {ctx.author.mention}')
        await cur.close()
        db.close()
        await message.edit(content=f'{count} members added to the database! {ctx.author.mention}')

Теперь, это работает отлично, единственная проблема, с которой я сталкиваюсь, это то, что я выполняю новый запрос SELECT для каждой итерации моего l oop. Поэтому я хотел бы поместить оператор SELECT вне l oop, используя cur.fetchall(), чтобы поместить его в список (см. Закомментированные строки в коде выше), но после этого я не знаю, как проверить, пара (member.id,str(member)) в моих результатах.

Ответы [ 2 ]

1 голос
/ 04 мая 2020

Рассмотрим один запрос на вставку в l oop, используя хорошо известное избежание дублирования в SQL: NOT IN vs. NOT EXISTS vs. LEFT JOIN / IS NULL. Ниже описан подход LEFT JOIN / IS NULL:

for member in ctx.guild.members: 
    try: 
        sql = """INSERT INTO aliases (discord, name) 
                 SELECT a1.discord, a1.name
                 FROM aliases a1
                 LEFT JOIN aliases a2
                    ON a1.discord = a2.discord
                    AND a1.name = a2.name
                    AND a1.discord=%s AND a2.name=%s
                 WHERE a2.name IS NULL AND a2.discord IS NULL
             """
        result = await cur.execute(sql, (member.id, str(member)))
        count = cur.rowcount
        await db.commit()

    except:
        await ctx.send(f'Error adding `{member}`! {ctx.author.mention}') 

Еще лучше заполнить идентификаторы членов во временной таблице и присоединиться к вышеуказанному запросу только для one запрос для всех идентификаторов членов. Обработка на основе множеств SQL превосходит циклы прикладного уровня!

# CLEAN AND POPULATE TEMP TABLE
await cur.execute("DELETE FROM mytempTable")
members_list = [(member.id, str(member)) for member in ctx.guild.members]
await cur.executemany("INSERT INTO mytempTable (discord, name) VALUES (%s, %s)", 
                      members_list)
await db.commit()

# ONLY ONE INSERT QUERY
sql = """INSERT INTO aliases (discord, name) 
         SELECT a1.discord, a1.name
         FROM aliases a1
         INNER JOIN mytempTable t
            ON a1.discord = t.discord
            AND a1.name = t.name
         LEFT JOIN aliases a2
            ON a1.discord = a2.discord
            AND a1.name = a2.name
         WHERE a2.name IS NULL AND a2.discord IS NULL
     """
result = await cur.execute(sql, (member.id, str(member)))
count = cur.rowcount
await db.commit()
0 голосов
/ 05 мая 2020

Полученный из ответа Парфе, я получил следующее:

async def loadnames(self, ctx):
    """
    Load the current alias of every member in the database.
    Useful after joining a new guild or after a long downtime.
    """
    async with ctx.message.channel.typing():
        count = 0
        message = await ctx.send(f'Adding members to the database... {ctx.author.mention}')

        db = await aiomysql.connect(host=globals.mysql_host,user=globals.mysql_user,password=globals.mysql_passwd,db=globals.mysql_db)
        cur = await db.cursor()
        await cur.execute("SELECT discord, name FROM aliases")
        result = await cur.fetchall()

        new_member_list = [(member.id, str(member)) for member in ctx.guild.members]
        member_list = [(member[0],member[1]) for member in result]
        diff_list = [member for member in new_member_list if member not in member_list]

        count = await cur.executemany("INSERT INTO aliases (discord, name) VALUES (%s, %s)", diff_list)
        await db.commit()
        await cur.close()
        db.close()

        if count is None: count = 0
        await message.edit(content=f'{count} members added to the database! {ctx.author.mention}')

Вначале только один запрос SELECT, затем мы поиграем с различными списками, чтобы получить оператор executemany INSERT, который будет делать только необходимое количество вставок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...