python - 为什么使用参数的 pandas.read_sql 比使用内联参数慢得多

标签 python sql-server python-3.x pandas pyodbc

我有 python 中的 cgi 页面,它使用 pandas 与 SQL Server 中的数据进行交互。

摘要

查询是基于用户与另一个查询中的某些其他数据的交互来运行的。这些都是使用 pandas.read_sql() 加载的功能。由于某种原因,在 python 中与直接在数据库(在 SQL Server Management Studio 中)上运行时进行比较时,第二个查询的运行速度比应有的速度要慢得多。经过一些测试,我发现当我使用params=[p]传递参数时查询速度要慢得多。 ,我最初正在这样做,并且更愿意这样做,而不是内联在查询中(下面的代码)。我不确定为什么会这样,并认为有人可能有想法。

代码

#Method 1: using param=[] 
query = "select * from FloorPlans where hydroid = ? order by plan_date desc"
t1 = datetime.datetime.now()
df2 = pd.read_sql(query, conn, params=[row["HydroID"]])
t2 = datetime.datetime.now()
print(t2-t1)

#Method 2: inline
query = "select * from FloorPlans where hydroid = '" + row["HydroID"] + "' order by plan_date desc"
t3 = datetime.datetime.now()
df2 = pd.read_sql(query, conn)
t4 = datetime.datetime.now()
print(t4-t3)

次数

方法 1:~210.0 秒

方法 2:~0.05 秒

在 SSMS 中:~0.04 秒


有人知道为什么会发生这种情况吗?我已经检查以确保 param 方法按预期发送字符串(通过将其包装在 str() 中),并且我已经检查了各种值。我在 Hydroid 列上有一个聚集索引,但这并不重要,因为它在所有三种情况下都是相同的值。我还有另外两个查询在其他表上执行几乎相同的操作(在具有聚集索引的 varchar 列上选择 *),并且它们没有这个问题。

到目前为止,我唯一能想到的是,在 FloorPlans 表中,Hydrioid 目前始终是一个数字序列(这可能会在将来发生变化,因为包含相同标识符的其他表已经具有字母数字 Hydroids 的实例),尽管确保变量是字符串,但 pandas 中的某些内容会在发送到 SQL 之前将其转换回 int,这会导致查询出现问题。

最佳答案

如果您使用的是 Python_3,则所有字符串都是 Unicode。 Python 代码...

sql = "SELECT * FROM MillionRows WHERE varchar_column = ?"
crsr.execute(sql, 'record012345')

...在 SQL Server 上处理为

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@P1 nvarchar(24)',N'SELECT * FROM MillionRows WHERE varchar_column = @P1',N'record012345'
select @p1

请注意,参数值是 Unicode:nvarchar(24)

现在,如果我们检查 SSMS 中等效查询的实际执行计划...

SELECT * FROM MillionRows WHERE varchar_column = N'record012345'

...我们看到了

   Physical operation: Index Scan
Actual Number of Rows: 1
  Number of Rows Read: 1000000

另一方面,如果我们运行使用 varchar 值的查询...

SELECT * FROM MillionRows WHERE varchar_column = 'record012345'

...执行计划向我们展示了

   Physical operation: Index Seek
Actual Number of Rows: 1
  Number of Rows Read: 1

差异是由于第一个查询必须针对 varchar 索引扫描(隐式转换的)nvarchar 值,而第二个查询是能够执行“查找”而不是“扫描”。

原始 Python 代码的修复方法是使用 setinputsizes指定查询参数应该是 varchar ...

sql = "SELECT * FROM MillionRows WHERE varchar_column = ?"
crsr.setinputsizes([(pyodbc.SQL_VARCHAR, 25)])
crsr.execute(sql, 'record012345')

.. 在 SQL Server 上处理为

declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@P1 varchar(25)',N'SELECT * FROM MillionRows WHERE varchar_column = @P1','record012345'
select @p1

pandas read_sql_query 的解决方法是在 SQL 查询本身中CAST 参数值

sql = "SELECT * FROM MillionRows WHERE varchar_column = CAST(? AS varchar(25))"
df = pd.read_sql_query(sql, engine, params=['record012345'])

关于python - 为什么使用参数的 pandas.read_sql 比使用内联参数慢得多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58584527/

相关文章:

sql - 如何从 SQL Server 中的字符串中删除最后一个符号。如果它是一个循环

sql-server - 如何从存储过程返回 ID 数组?

sql-server - 使用 Entity Framework Database.Create() 时指定 SQL Server 文件位置

string - 固定宽度数字格式化python 3

python - 对于范围内的我(len(名字)): print(firstname[0]) - How do i print this in one line?

python - 如何获取 Pandas 中 groupby 对象中每个项目的索引?

python - 根据属性之和将数组拆分为数组数组

python-3.x - 使用 flask 路由(Python)启动和停止方法

python - len(轮廓)是什么意思?

python - 比使用集合更快的比较字典的方法