SQL绑定参数会影响性能吗



假设我有一个名为Projects的表,其中有一个列Budget,带有标准的B-Tree索引。该表有5万个项目,其中只有1%的项目预算超过100万。如果我运行SQL查询:

SELECT * From Projects WHERE Budget > 1000000;

计划器将在Budget上使用索引范围扫描来从堆表中获取行。但是,如果我使用查询:

SELECT * From Projects WHERE Budget > 50;

计划器很可能会对表进行顺序扫描,因为它知道这个查询最终会返回大部分或所有行,并且没有理由将索引的所有页面加载到内存中。

现在,假设我运行查询:

SELECT * From Projects WHERE Budget > :budget;

其中:budget是传递到我的数据库中的绑定参数。根据我所读到的内容,上面的查询将被缓存,并且不能推断出基数的数据。事实上,大多数数据库只是假设分布均匀,缓存的查询计划将反映这一点。这让我很惊讶,通常当你读到绑定参数的好处时,它是关于防止SQL注入攻击的。

显然,如果生成的查询计划相同,则这可以提高性能,因为不必编译新计划,但如果:budget的值变化很大,则也可能损害的性能。

我的问题:为什么在生成和缓存查询计划之前,绑定参数没有得到解决?现代数据库难道不应该努力为查询生成最佳计划吗?这意味着要查看每个参数的值并获得准确的索引统计数据?

注意:这个问题可能不适用于mySql,因为mySql不缓存SQL计划。然而,我感兴趣的是为什么Postgres、Oracle和MSSQL会出现这种情况。

具体来说,这取决于Oracle。

很长一段时间以来(至少9i),Oracle一直支持绑定变量窥视。这意味着,第一次执行查询时,优化器会查看绑定变量的值,并根据第一个绑定变量的价值来估计基数。在大多数查询执行都将具有返回大小相似结果的绑定变量值的情况下,这是有意义的。如果99%的查询都使用小预算值,那么第一次执行很可能会使用小值,因此缓存的查询计划将适用于小绑定变量值。当然,这意味着当您确实指定了一个大的绑定变量值时(或者,更糟糕的是,如果您运气好,并且第一次执行是使用一个大值),您将得到低于最佳的查询计划。

如果使用11g,Oracle可以使用自适应光标共享。这允许优化器为单个查询维护多个查询计划,并根据绑定变量值选择适当的计划。不过,随着时间的推移,情况可能会变得相当复杂。如果您有一个具有N个绑定变量的查询,优化器必须弄清楚如何将该N维空间划分为不同绑定变量值的不同查询计划,以便弄清楚何时以及是否为一组新的绑定变量值重新优化查询,以及何时简单地重用早期计划。许多工作最终都是在夜间维护窗口的夜间完成的,以避免在生产日产生这些成本。但这也带来了一个问题,即DBA希望给数据库多大的自由来随着时间的推移发展计划,而DBA希望控制计划的程度,这样数据库就不会突然开始选择一个糟糕的计划,导致一些主要系统在随机的一天慢下来爬行。

这让我很惊讶,通常当你读到绑定参数的好处时,它是关于防止SQL注入攻击的。

不要将参数化查询准备好的语句混淆。两者都提供了参数化,但prepared语句提供了查询计划的额外缓存。

为什么在生成和缓存查询计划之前没有解析绑定参数?

因为有时生成查询计划是一个昂贵的步骤。准备好的报表允许您摊销查询计划的成本。

但是,如果您所要查找的只是SQL注入保护,则不要使用准备好的语句。使用参数化查询。

例如,在PHP中,您可以使用http://php.net/pg_query_params在不缓存所述查询计划的情况下执行参数化查询;同时http://php.net/pg_prepare和http://php.net/pg_execute用于为准备好的语句缓存计划,然后执行它。

编辑:9.2显然改变了计划编制报表的方式

最新更新