当前位置: 首页 > 面试题库 >

为什么将参数化后查询会中断?

慕鸿波
2023-03-14
问题内容

我有2张桌子-SalesProductSales可以将产品存储为 IdnName
(旧式设计),并且该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匹配的行,则可以正常工作 (1)
。例如,如果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中尝试此操作时,它给了我一个数据类型转换错误,并且仅在对查询进行 参数化 时才会发生!

现在,如果我使用u'number'Python 2制作它,它也会在那里中断。并b'number'可以在Python
3中使用!我猜测Unicode转换存在一些问题。它是在尝试 猜测 编码并做错了什么吗?我可以更明确地解决此问题吗?

收到的错误是:

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(因为它在以前的版本中有效)?

这是一个可用于重现此问题而没有副作用(需要SQLAlchemyPyODBC)的查询:

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()

1. 这是一个错误的假设。它的工作是偶然-
SQL的执行计划优先考虑这种情况作为解释在这里。它变成时并没有这样做NVARCHAR


问题答案:

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上尝试此操作,则会得到相同的异常:

消息8114,级别16,状态5,第32行将数据类型varchar转换为数值时出错。

问题出在@P1参数声明上-它隐式转换为varchar(的类型sales.type),从而导致此问题。可能是Python 2生成了varchar?

如果您像这样更改查询,它将可以正常工作;或您需要将的类型更改sales.typenvarchar

select_parametrized = """
SELECT * FROM products
INNER JOIN sales ON products.idn = sales.pid 
    AND sales.type = CAST(? AS VARCHAR(50))
WHERE products.idn in (?);
"""


 类似资料:
  • 问题内容: 什么是参数化查询?在PHP和MySQL中,这种查询的示例是什么? 问题答案: 参数化查询(也称为 预处理语句)是预编译SQL语句的一种方法,因此您需要提供的只是需要插入语句中的“参数”(认为“变量”)即可。被执行。它通常用作防止SQL注入攻击的手段。 您可以在PHP的PDO页面(PDO是数据库抽象层)上阅读有关这些内容的更多信息,但是如果您使用的是mysqli数据库接口,也可以使用它们

  • 问题内容: 我听说“每个人”都在使用参数化的SQL查询来防止SQL注入攻击,而不必验证用户的每一项输入。 你怎么做到这一点?使用存储过程时,是否会自动获得此信息? 所以我的理解是非参数化的: 可以将其参数化吗? 还是我需要像这样做一些更广泛的事情,以保护自己免受SQL注入的侵害? 除了安全方面的考虑之外,使用参数化查询还有其他优势吗? 更新:这篇很棒的文章与Grotok引用的问题之一相关联。 ht

  • 问题内容: 我很难使用MySQLdb模块将信息插入到我的数据库中。我需要在表中插入6个变量。 有人可以帮我这里的语法吗? 问题答案: 提防对SQL查询使用字符串插值,因为它不能正确地转义输入参数,并使您的应用程序容易受到SQL注入漏洞的影响。 这种差异看似微不足道,但实际上它是巨大的 。 不正确(存在安全问题) 正确(带有转义符) 这增加了混乱,即用于绑定SQL语句中的参数的修饰符在不同的DB A

  • 问题内容: 我有一个查询,我试图填充我要参数化的CFChart: 这是我尝试过的: 当我将查询更改为此时,它将以某种方式破坏CFChart。屏幕上没有出现任何CFError,但是我的CFChart为空白。 我在查询中将其范围缩小到与此相关: 当我删除查询的此参数化部分并放入 有用。 谁能对此有所启发? 问题答案: 屏幕上没有出现任何CFError,但是我的CFChart为空白。 暂时忽略正确的方法

  • 我开始了解axiosendpoint,我想知道为什么我们要对endpoint使用查询参数? 谷歌表示,我们之所以使用它,是因为它“有助于检索特定数据,并根据用户传递的输入执行操作” 我相信有一个“过滤”函数可以用来过滤响应,我假设我们可以使用查询参数来做类似的事情,但我不确定它们是否就是这个意思。我创建了一个客户机和服务器示例,在该示例中,我将查询参数传递给服务器,服务器返回一个带有我发送的参数的

  • 问题内容: 我很难使用MySQLdb模块将信息插入到数据库中。我需要在表中插入6个变量。 有人可以帮我这里的语法吗? 问题答案: 提防对SQL查询使用字符串插值,因为它不能正确地转义输入参数,并使您的应用程序容易受到SQL注入漏洞的影响。这种差异看似微不足道,但实际上它是巨大的。 不正确(存在安全问题) 正确(带有转义符) 这增加了混淆,用于绑定SQL语句中的参数的修饰符在不同的DB API实现之