Мне удалось воспроизвести проблему.Ваш MCVE был очень полезен.
Интересно было видеть, что для вашего примера ORM SQL Profiler не показал никаких доказательств того, что SQLAlchemy извлекал метаданные столбца перед выполнением запроса SELECT к таблице.Очевидно, он полагает, что знает достаточно о столбцах для построения рабочего запроса, хотя (как выясняется) он не обязательно самый эффективный.
Я знал, что SQL-язык выражений SQLAlchemy получит таблицуметаданных, поэтому я попробовал аналогичный SELECT, используя
metadata = MetaData()
my_table = Table('test', metadata, autoload=True, autoload_with=engine)
stmt = select([my_table.c.id, my_table.c.key])\
.select_from(my_table)\
.where(my_table.c.key == value)
cnxn = engine.connect()
items = cnxn.execute(stmt).fetchall()
, и хотя SQLAlchemy действительно извлек метаданные, используя
SELECT [INFORMATION_SCHEMA].[columns].[table_schema],
[INFORMATION_SCHEMA].[columns].[table_name],
[INFORMATION_SCHEMA].[columns].[column_name],
[INFORMATION_SCHEMA].[columns].[is_nullable],
[INFORMATION_SCHEMA].[columns].[data_type],
[INFORMATION_SCHEMA].[columns].[ordinal_position],
[INFORMATION_SCHEMA].[columns].[character_maximum_length],
[INFORMATION_SCHEMA].[columns].[numeric_precision],
[INFORMATION_SCHEMA].[columns].[numeric_scale],
[INFORMATION_SCHEMA].[columns].[column_default],
[INFORMATION_SCHEMA].[columns].[collation_name]
FROM [INFORMATION_SCHEMA].[columns]
WHERE [INFORMATION_SCHEMA].[columns].[table_name] = Cast(
N'test' AS NVARCHAR(max))
AND [INFORMATION_SCHEMA].[columns].[table_schema] = Cast(
N'dbo' AS NVARCHAR(max))
ORDER BY [INFORMATION_SCHEMA].[columns].[ordinal_position]
, часть выходных данных которых равна
TABLE_SCHEMA TABLE_NAME COLUMN_NAME IS_NULLABLE DATA_TYPE ORDINAL_POSITION CHARACTER_MAXIMUM_LENGTH
------------ ---------- ----------- ----------- --------- ---------------- ------------------------
dbo test id NO int 1 NULL
dbo test key NO varchar 2 50
в результирующем запросе SELECT по-прежнему использовался nvarchar
литерал
SELECT test.id, test.[key]
FROM test
WHERE test.[key] = N'record123456'
Наконец, я проделал те же тесты, используя pyodbc
вместо pymssql
, и результаты были практически одинаковыми.Мне было любопытно, если бы диалект SQLAlchemy для pyodbc мог использовать преимущества setinputsizes для указания типов параметров (т. Е. pyodbc.SQL_VARCHAR
вместо pyodbc.SQL_WVARCHAR
), но, по-видимому, это не так.
ТакЯ бы сказал, что на данный момент лучше всего продолжать кодировать строковые значения в байтах, которые соответствуют набору символов столбца varchar
, который вы запрашиваете (не utf-8).Конечно, вы также можете погрузиться в исходный код для диалекта (ов) SQLAlchemy и отправить PR, чтобы улучшить SQLAlchemy.