SQLAlchemy ON DUPLICATE KEY UPDATE

Есть ли элегантный способ сделать INSERT .. . ON DUPLICATE KEY UPDATE в SQLAlchemy? Я имею в виду что-то с синтаксисом, похожим на insertter.insert (). Execute (list_of_ dictionaries) ?

29
задан MrD 7 July 2011 в 13:43
поделиться

4 ответа

ON DUPLICATE KEY UPDATE после версии 1.2 для MySQL

Эта функциональность теперь встроена только в SQLAlchemy для MySQL. Ответ somada141 ниже имеет лучшее решение: https://stackoverflow.com/a/48373874/319066

ON DUPLICATE KEY UPDATE в операторе SQL

Если вы хотите сгенерированный SQL на самом деле включает в себя ON DUPLICATE KEY UPDATE, самый простой способ заключается в использовании декоратора @compiles.

Код (связанный с хорошей веткой по теме на Reddit ) для примера можно найти на github :

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Insert

@compiles(Insert)
def append_string(insert, compiler, **kw):
    s = compiler.visit_insert(insert, **kw)
    if 'append_string' in insert.kwargs:
        return s + " " + insert.kwargs['append_string']
    return s


my_connection.execute(my_table.insert(append_string = 'ON DUPLICATE KEY UPDATE foo=foo'), my_values)

Но обратите внимание, что при таком подходе вы должны вручную создать строку append_string. Возможно, вы могли бы изменить функцию append_string, чтобы она автоматически изменяла строку вставки на вставку со строкой «ON DUPLICATE KEY UPDATE», но я не собираюсь делать это здесь из-за лени.

ON DUPLICATE KEY UPDATE функциональность в ORM

SQLAlchemy не предоставляет интерфейс к ON DUPLICATE KEY UPDATE или MERGE или любой другой подобной функциональности на своем уровне ORM. Тем не менее, он имеет функцию session.merge() , которая может воспроизводить функциональность , только если рассматриваемый ключ является первичным ключом . ​​

session.merge(ModelObject) сначала проверяет, существует ли строка с тем же значением первичного ключа, отправляя запрос SELECT (или просматривая его локально). Если это так, он устанавливает флаг где-то, указывая, что ModelObject уже находится в базе данных, и что SQLAlchemy должен использовать запрос UPDATE. Обратите внимание, что слияние немного сложнее, чем это, но оно хорошо копирует функциональность с первичными ключами.

Но что, если вам нужна функциональность ON DUPLICATE KEY UPDATE с неосновным ключом (например, другим уникальным ключом)? К сожалению, в SQLAlchemy такой функции нет. Вместо этого вы должны создать что-то похожее на Джанго get_or_create(). Другой ответ StackOverflow покрывает его , и я просто вставлю здесь модифицированную рабочую версию для удобства.

def get_or_create(session, model, defaults=None, **kwargs):
    instance = session.query(model).filter_by(**kwargs).first()
    if instance:
        return instance
    else:
        params = dict((k, v) for k, v in kwargs.iteritems() if not isinstance(v, ClauseElement))
        if defaults:
            params.update(defaults)
        instance = model(**params)
        return instance
39
ответ дан 28 November 2019 в 01:33
поделиться

На основе ответа источника phs и для конкретного случая использования MySQL и полного переопределения данных для того же ключа без выполнения оператора DELETE можно использовать следующие @compiles украшенные вставки выражения:

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Insert

@compiles(Insert)
def append_string(insert, compiler, **kw):
    s = compiler.visit_insert(insert, **kw)
    if insert.kwargs.get('on_duplicate_key_update'):
        fields = s[s.find("(") + 1:s.find(")")].replace(" ", "").split(",")
        generated_directive = ["{0}=VALUES({0})".format(field) for field in fields]
        return s + " ON DUPLICATE KEY UPDATE " + ",".join(generated_directive)
    return s
1
ответ дан 28 November 2019 в 01:33
поделиться

Получил более простое решение:

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Insert

@compiles(Insert)
def replace_string(insert, compiler, **kw):
    s = compiler.visit_insert(insert, **kw)
    s = s.replace("INSERT INTO", "REPLACE INTO")
    return s

my_connection.execute(my_table.insert(replace_string=""), my_values)
1
ответ дан 28 November 2019 в 01:33
поделиться

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

-2
ответ дан 28 November 2019 в 01:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: