Подтвердить что ты не робот

Как связать список с параметром в пользовательском запросе в 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'?

4b9b3361

Ответ 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 будет делать в крайнем случае.