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:
Преобразуйте вашу структуру данных в словарь. Из Панд это
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')
Подключитесь к базе данных через 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()
Выполнить
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()