Как связать список с параметром в пользовательском запросе в sqlalchemy?
Я использую этот sql для повышения производительности
sql_tmpl = """delete from Data where id_data in (:iddata) """
params = {
'iddata':[1, 2,3 4],
}
# session is a session object from sqlalchemy
self.session.execute(text(sql_tmpl), params)
Однако я получил исключение
NotSupportedError: (NotSupportedError) ('Python type list not supported. param=1', 'HY097')
Есть ли какое-либо обходное решение, которое может позволить мне привязать список к параметру предложения 'in'?
Ответы
Ответ 1
Новый ответ на старый вопрос, потому что кажется, что некоторые из базовых функций изменились, поскольку этот вопрос/принятый ответ были впервые отправлены (как указано в @vicvicvic в ответе @Gary, но я считаю, что это должен быть ответ для лучшая видимость).
psycopg2
теперь поддерживает тип адаптации, что позволяет, среди прочего, передавать список в одно параметризованное значение в запрос. Это также работает в SQLAlchemy, по крайней мере, для запросов raw-SQL-esque для базы данных postgresql (у меня нет доступа к другим типам баз данных, поэтому я не знаю, будет ли sqlalchemy
соблюдать это соглашение для других баз данных, но моя склонность которая нужна заключается в том, что она будет работать).
some_ids = [1, 2, 3, 4]
query = "SELECT * FROM my_table t WHERE t.id = ANY(:ids);"
conn.execute(sqlalchemy.text(query), ids=some_ids)
## runs just fine
Я обнаружил, что без вызова обертки sqlalchemy.text
он дал ProgrammingError: syntax error at or near ":"
.
Ответ 2
Новый новый подход к этой проблеме, который работает для любой базы данных (не просто полагаясь на адаптацию типа psycopg2), использует расширяющиеся параметры связывания:
sql_tmpl = """delete from Data where id_data in :iddata"""
params = { 'iddata': [1, 2, 3, 4], }
# session is a session object from sqlalchemy
t = text(sql_tmpl)
t = t.bindparams(bindparam('iddata', expanding=True))
self.session.execute(t, params)
Ответ 3
Попробуйте без парнеров: iddata, который работал у меня.
sql_tmpl = """delete from Data where id_data in :iddata """
Ответ 4
Насколько мне известно, ни один из движков sql не позволяет передавать параметр массива. Способ, которым sqlalchemy обрабатывает это, - передать параметр для каждого элемента массива.
>>> from sqlalchemy.sql import table, column
>>> print(table('Data').delete(column('id_data').in_([5, 6, 7,])))
DELETE FROM "Data" WHERE id_data IN (:id_data_1, :id_data_2, :id_data_3)
Если вы не используете конструкции выражения sqlalchemy, вам нужно будет сделать это вручную.
Ответ 5
Вы можете сгенерировать предложение where, используя цикл, и использовать **, чтобы разбить список в параметрах query.execute. Вот пример: https://gist.github.com/pawl/555e5eecce77d4de0ada
Ответ 6
Добавление в dwanderson ответа: с помощью SQLAalchemy вы можете использовать func для добавления к запросу функции "any". Это работает для меня, используя SQLAlchemy 1.0.9 и DB Postgres.
Общий пример:
from sqlalchemy import func
# some list
id_list = [1, 2, 3]
# assuming you have created a session
query = session.query(Table.user_name, Table.user_id).\
filter(Table.user_id == func.any(id_list))
# one way of running the query
query.all()
Вы можете проверить, что список передается как один параметр (в отличие от параметра для каждого объекта в списке).
print(query.statement)
SELECT user_id, имя_пользователя FROM table WHERE table.user_id = any (: any_1)
Ответ 7
В Microsoft SQL Server вы можете использовать табличные параметры для достижения того же результата.
SELECT * FROM table_name WHERE customer_id in (SELECT * FROM @MyTVP)
TVP в настоящее время поддерживается только PyTDS, но не PyODBC. Флаг expanding=True
подробно описанный Джейсоном Дамиани, является, вероятно, лучшим способом сделать это, если у вас более новая версия SQLAlchemy. Но TVP будет делать в крайнем случае.