Postgresql ON CONFLICT в sqlalchemy

Я прочитал довольно много ресурсов (ао. 1, 2), но я не могу заставить поведение Postgresql ON CONFLICT IGNORE работать в sqlalchemy.

Я использовал этот принятый ответ в качестве основы, но он дает

SAWarning: Can't validate argument 'append_string'; can't locate any SQLAlchemy dialect named 'append'

Я пытался добавить диалект postgresql в предложение @compile, переименовывая мой объект, но он не работает. Я также пытался использовать str(insert())+ " ON CONFILCT IGNORE" безрезультатно. (не удивительно, между прочим)

Как я могу получить On CONFLICT IGNORE для добавления к моим вставкам? Мне нравится предлагаемое решение, так как я вижу, что не желаю поведения IGNORE на каждом INSERT

пс. использование Python 2.7 (не против обновления до 3.4/3.5), последняя версия sqlalchemy (1.x)

Ответ 1

Вам это не нужно, используйте условие "существует" для предотвращения вставки дубликатов.

например:

INSERT INTO table (unique_name) 
SELECT 'some_string'
WHERE NOT EXISTS(SELECT 1 FROM table WHERE unique_name = 'some_string')

вы также можете

INSERT INTO table (unique_name)
VALUES('some_string')
ON CONFLICT (unique_name) DO NOTHING

но если вам нужно вставить или обновить в одном запросе, то это пример для вас:

INSERT INTO distributors (did, dname)
VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc')
ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname;

Это пример из документации PostgreSQL.

Ответ 2

Использование Postgres 9.6.1, sqlachemy 1.1.4 и psycopg2 2.6.2:

  1. Преобразуйте вашу структуру данных в словарь. Из Панд это

    import pandas
    from sqlalchemy import MetaData
    from sqlalchemy.dialects.postgresql import insert
    import psycopg2
    
    # The dictionary should include all the values including index values
    insrt_vals = df.to_dict(orient='records')
    
  2. Подключитесь к базе данных через sqlalchemy. Вместо этого попробуйте драйвер psycog2 и встроенную функцию COPY, которая обходит все индексации postgres.

    csv_data = os.path.realpath('test.csv')

    con = psycopg2.connect(database = 'db01', user = 'postgres') cur = con.cursor()

    cur.execute("\ копировать данные stamm_data из '% s' DELIMITER ';' заголовок csv"% csv_data)

    con.commit()

  3. Выполнить

    results = engine.execute(do_nothing_stmt)
    # Get number of rows inserted
    rowcount = results.rowcount
    

Внимание:

Этот метод не работает с NaT из коробки.

Все вместе

tst_df = pd.DataFrame({'colA':['a','b','c','a','z', 'q'],
              'colB': pd.date_range(end=datetime.datetime.now() , periods=6),
              'colC' : ['a1','b2','c3','a4','z5', 'q6']})


insrt_vals = tst_df.to_dict(orient='records')
engine =      sqlalchemy.create_engine("postgresql://user:[email protected]/postgres")
connect = engine.connect()
meta = MetaData(bind=engine)
meta.reflect(bind=engine)
table = meta.tables['tstbl']
insrt_stmnt = insert(table).values(insrt_vals)

do_nothing_stmt  = insrt_stmnt.on_conflict_do_nothing(index_elements=['colA','colB'])
results = engine.execute(do_nothing_stmt)

Вместо шагов 2 и 3 использование драйвера psycog2 с командой copy в postgres быстрее для больших файлов (приближается к гигу), поскольку отключает индексацию всей таблицы.

csv_data = os.path.realpath('test.csv')

Ответ 3

Это работает с Postgresql 9.5:

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

@compiles(Insert)
def prefix_inserts(insert, compiler, **kw):
    return compiler.visit_insert(insert, **kw) + " ON CONFLICT DO NOTHING"

Я использую его для bulk_insert_mappings. Однако он не делает ON CONFLICT DO NOTHING необязательным

Ответ 4

Это работает с Postgresql 10.5 и Sqlalchemy 1.3.6:

from sqlalchemy.dialects.postgresql import insert


table_info = {
'tableTime': '',
'deploymentID': '',
'tableData': ''
}
insert_table = insert(Table).values(table_info)
insert_table_sql = insert_table.on_conflict_do_nothing(
  index_elements=['tableTime', 'deploymentID']
)
db.session.execute(insert_table_sql)
db.session.commit()