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

通过添加未使用的WHERE条件,查询运行时间更长

裴嘉良
2023-03-14
问题内容

我遇到了一个有趣的障碍(至少对我来说很有趣)。以下是查询的大致概念。假设@AuthorType是存储过程的输入,并且我在每个地方添加了注释,都有各种特殊条件。

SELECT *
FROM TBooks
WHERE
(--...SOME CONDITIONS)
OR
(@AuthorType = 1 AND --...DIFFERENT CONDITIONS)
OR
(@AuthorType = 2 AND --...STILL MORE CONDITIONS)

对我来说有趣的是,如果我使用@AuthorType =
0执行此SP,则它的运行速度比如果删除最后两组条件(为@AuthorType的特殊值添加条件的条件)要慢。

SQL Server难道不应该在运行时意识到永远不会满足这些条件并完全忽略它们吗?我所经历的差异并不小;大约是查询时间的两倍(1-2秒到3-5秒)。

我是否期望SQL Server对我进行太多优化?我是否真的需要3个单独的SP用于特殊条件?


问题答案:

SQL Server难道不应该在运行时意识到永远不会满足这些条件并完全忽略它们吗?

不,绝对不是。这里有两个因素在起作用。

  1. SQL Server不会 保证布尔运算符短路。有关示例,请参阅“在SQL Server上布尔运算符短路”中清楚地显示了查询优化如何颠倒布尔表达式求值的顺序。乍一看,这似乎是命令式C语言编程心态的一个错误,但对于面向声明性集的SQL世界而言,这是正确的做法。

  2. OR是SQL SARGability的敌人。将SQL语句编译为执行计划,然后执行该计划。该计划在调用之间被重用(被缓存)。因此,SQL编译器必须生成一个适合所有单独OR情况的计划(@ AuthorType = 1 AND @ AuthorType = 2 AND @ AuthorType = 3)。从某种意义上说,当涉及到生成查询计划时,就好像@AuthorType一次具有所有值一样。结果几乎总是最糟糕的计划,因为各种OR分支相互矛盾,所以该计划无法使任何索引受益,因此最终导致扫描整个表并逐行检查行。

对于您的情况以及任何其他涉及布尔OR的情况,最好的做法是将@AuthorType移至查询之外:

IF (@AuthorType = 1)
  SELECT ... FROM ... WHERE ...
ELSE IF (@AuthorType = 2)
  SELECT ... FROM ... WHERE ...
ELSE ...

因为每个分支都清楚地分成了自己的语句,所以SQL可以为每种情况创建正确的访问路径。

第二个最好的方法是使用chadhoc已经建议的UNION ALL,并且在需要单个语句(不允许IF)的视图或其他地方使用正确的方法。



 类似资料:
  • WHERE 语句 SQL中使用where可能会有一些不安全的动态参数传入或者一些复杂的SQL语句,但是Medoo提供非常简介和安全的方法来实现这些. 基础使用 在基础使用中. 你可以使用一些符号对参数进行过滤 $database->select("account", "user_name", [ "email" => "foo@bar.com"]);// WHERE email = 'foo

  • WHERE 语句 SQL中使用where可能会有一些不安全的动态参数传入或者一些复杂的SQL语句,但是Medoo提供非常简介和安全的方法来实现这些. 基本使用 在基本使用中. 你可以使用一些符号对参数进行过滤 $database->select("account", "user_name", [ "email" => "foo@bar.com"]);// WHERE email = 'foo@ba

  • 前面介绍了如何对表数查询、更新、删除,本小节介绍如何在查询、更新、删除操作加上 WHERE 条件约束,使这些语句的操作更加准确,满足业务需求。WHERE 条件的操作符类型有如下: 符号 说明 举例 < 小于,< 左边的值如果小于右边的值,则结果为 TRUE,否则为 FALSE 如 : 满足年龄小于 18 的条件 age < 18 = 等于,= 左边的值如果等于右边的值,则结果为 TRUE,否则为

  • 我遇到了一个奇怪的问题,使用Oracle Pivot语法的sql查询。我可以毫无问题地在SqlDeveloper中运行查询;然而,使用行映射器通过JdbcTemplate运行它会产生关于无效列名的奇怪错误。 SQL语句: 行映射器: DAO调用类:

  • 我想更新索引中的所有文档。我发现更新查询是我们应该使用的方法。但是,当我使用ctx._now作为更新文档字段的值时,我遇到了问题,导致字段值变为NULL。 这是示例: 当我使用随机数值时,它是工作。假设我把timenow=5。然后,All documents字段timenow变为5。但是,使用这种ctx方法是行不通的。 我该怎么做呢? 附加信息 示例:POST INDEX/TYPE/24/_UPD

  • 问题内容: 我想在查询中添加一个子句,但 有条件地 。具体来说,我只希望在URL中传递单独的querystring参数时才添加它。这可能吗?如果可以,我将如何去做呢? 问题答案: 您可以将查询存储在变量中,应用条件where子句,然后执行它,如下所示: