我有 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/