SELECT * FROM products
JOIN sales ON products.idn = sales.pid
AND sales.type = 'number'
WHERE products.idn in (1);
但是使用参数化查询会生成此(select_parametrized)。 (我从SQL Server Profiler检查过)
我有2个表-Sales
和Product
。 Sales
可以将产品存储为Idn或Name(旧式设计),并且Type
列指定与之关联的实际type。 Product
等是该表的子集表,该子表联接到此表以获取真实数据。 (在此示例中,Product
是存储Idn的表以演示问题。)Sales
|------------|--------------------|----------------|
| Idn | Product Idn/Name | Type |
|------------|--------------------|----------------|
| 1 | 1 | Number |
|------------|--------------------|----- ----------|
| 2 | Colgate | Word |
|------------|--------------------|----------------|
Product (Idn)
|------------|------------------|
| Idn | Some Info |
|------------|------------------|
| 1 | ... |
|------------|------------------|
通常,您不应该在的表,则以下查询失败:Product Idn
上加入这些表,因为它包含混合数据;但是,如果您选择LHS与RHS相匹配的行,则可以正常工作。例如,如果Product
是存储Idn
SELECT * from sales JOIN product on sales.pid = product.idn
但是以下查询有效:
SELECT * from sales JOIN product on sales.pid = product.idn WHERE type = 'Number'
这也可以在Python 2 + SQLAlchemy + PyODBC中正常工作。但是,当我在Python 3 + SQLAlchemy + PyODBC中尝试此操作时,它给了我一个数据类型转换错误,并且仅在查询为[[parameterized!]]时才会发生
现在,如果我在Python 2中将其设为u'number'
,它也会在此处中断;并且b'number'
在Python 3中有效!我猜测Unicode转换存在一些问题。是否尝试guess编码并做错了什么?我可以更明确地解决此问题吗?
收到的错误是:Traceback (most recent call last):
File "reproduce.py", line 59, in <module>
print(cursor.execute(select_parametrized, ('number', 1)).fetchall())
pyodbc.ProgrammingError: ('42000', '[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Error converting data type varchar to numeric. (8114) (SQLFetch)
这里可能是问题所在,并且有没有解决convert
之类的问题的好方法(因为它在以前的版本中有效)?
这里是可用于重现此问题而没有副作用(需要SQLAlchemy
和PyODBC
)的查询:
import sqlalchemy
import sqlalchemy.orm
create_tables = """
CREATE TABLE products(
idn NUMERIC(9) PRIMARY KEY
);
CREATE TABLE sales(
idn NUMERIC(9) PRIMARY KEY,
pid VARCHAR(50) NOT NULL,
type VARCHAR(10) NOT NULL
);
"""
check_tables_exist = """
SELECT * FROM products;
SELECT * FROM sales;
"""
insert_values = """
INSERT INTO products (idn) values (1);
INSERT INTO sales (idn, pid, type) values (1, 1, 'number');
INSERT INTO sales (idn, pid, type) values (2, 'Colgate', 'word');
"""
select_adhoc = """
SELECT * FROM products
JOIN sales ON products.idn = sales.pid
AND sales.type = 'number'
WHERE products.idn in (1);
"""
select_parametrized = """
SELECT * FROM products
JOIN sales ON products.idn = sales.pid
AND sales.type = ?
WHERE products.idn in (?);
"""
delete_tables = """
DROP TABLE products;
DROP TABLE sales;
"""
engine = sqlalchemy.create_engine('mssql+pyodbc://user:password@dsn')
connection = engine.connect()
cursor = engine.raw_connection().cursor()
Session = sqlalchemy.orm.sessionmaker(bind=connection)
session = Session()
session.execute(create_tables)
try:
session.execute(check_tables_exist)
session.execute(insert_values)
session.commit()
print(cursor.execute(select_adhoc).fetchall())
print(cursor.execute(select_parametrized, ('number', 1)).fetchall())
finally:
session.execute(delete_tables)
session.commit()
我有2张桌子-销售和产品。销售人员可以将产品存储为Idn或Name(旧式设计),并且Type列指定与之关联的实际类型。产品等是......>
sqlalchemy使用您的非参数化查询(select_adhoc)生成此SQL脚本SELECT * FROM products JOIN sales ON products.idn = sales.pid AND sales.type = 'number' WHERE products.idn in (1);
但是使用参数化查询会生成此(select_parametrized)。 (我从SQL Server Profiler检查过)
declare @p1 int set @p1=NULL exec sp_prepexec @p1 output,N'@P1 nvarchar(12),@P2 int',N' SELECT * FROM products INNER JOIN sales ON products.idn = sales.pid AND sales.type = @P1 WHERE products.idn in (@P2); ',N'number',1 select @p1
并且如果您在SQL Server上尝试此操作,则会得到此异常。
Msg 8114,第16级,状态5,第32行将数据类型varchar转换为数字时出错。
问题出在@ P1参数声明处。它将隐式转换为varchar(sales.type的类型),并导致此问题。可能使用Python 2会生成varchar。并且如果您像这样更改查询,它将可以正常工作。或者,您需要将sales.type的类型更改为nvarchar。
select_parametrized = """ SELECT * FROM products INNER JOIN sales ON products.idn = sales.pid AND sales.type = CAST(? AS VARCHAR(50)) WHERE products.idn in (?); """