AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 77129
Accepted
User Smith
User Smith
Asked: 2014-09-20 13:24:59 +0800 CST2014-09-20 13:24:59 +0800 CST 2014-09-20 13:24:59 +0800 CST

Tsql查询速度慢由于Or inside where子句,导致索引扫描而不是seek

  • 772

我一直在努力使这个查询更有效地工作。

我发现 where 子句中 Ors 的数量是这个查询中最大的问题。此查询位于存储过程中。

我到了我能想到的唯一选择的地步。:

  • 为所有不同的入站参数可能性创建 16 个不同的查询。
  • 创建一个动态 sql 查询,但我不相信这会更快
  • 恢复到字符串 sql,但我不喜欢这样做,因为它们的执行速度不如存储过程。

我相信其他人之前遇到过这个问题。查询性能从大约一秒或更短的时间开始并不可怕,但在某些情况下,它被多次命中导致长达 5 或 6 秒的延迟。

下面查询。:

DECLARE @PERSON_ID AS INT
DECLARE @ITEM_ID AS INT
DECLARE @ITEM_VERSION AS INT
DECLARE @ITEM_SUB_NAME AS VARCHAR(250)
DECLARE @ITEM_SUB_SUB_NAME AS VARCHAR(250)

--DEFAULTS
SET @PERSON_ID = 0
SET @ITEM_ID = 0
SET @ITEM_VERSION = 1
SET @ITEM_SUB_NAME = NULL
SET @ITEM_SUB_SUB_NAME = NULL

    SELECT ID, PERSON_ID, 
           ISNULL(ITEM_VERSION, 1) AS ITEM_VERSION,
           ISNULL(ITEM_SUB_NAME, '') AS 'ITEM_SUB_NAME',
           ISNULL(ITEM_SUB_SUB_NAME, '') AS 'ITEM_SUB_SUB_NAME',
           ISNULL(ITEM_DATE, '1/1/1900') AS 'ITEM_DATE',
    FROM PERSON_TBL s WITH (NOLOCK)     
    WHERE    ( PERSON_ID = @PERSON_ID OR @PERSON_ID = 0 )
    AND ( ITEM_VERSION = @ITEM_VERSION OR ( @ITEM_VERSION = 1 AND ITEM_VERSION IS NULL ))
    AND ( EMPLOYEE_ID = @EMPLOYEE_ID OR @EMPLOYEE_ID = 0 )
    AND ( ITEM_SUB_NAME = @ITEM_SUB_NAME OR @ITEM_SUB_NAME IS NULL )
    AND ( ITEM_SUB_SUB_NAME = @ITEM_SUB_SUB_NAME OR @ITEM_SUB_SUB_NAME IS NULL )
    ORDER BY PERSON_ID, ITEM_SUB_NAME
sql-server t-sql
  • 1 1 个回答
  • 3865 Views

1 个回答

  • Voted
  1. Best Answer
    Aaron Bertrand
    2014-09-20T16:16:07+08:002014-09-20T16:16:07+08:00

    这就是我所说的“厨房水槽”存储过程——您需要一个过程来处理用户可能输入的所有可能的搜索条件组合。几乎不可能让 SQL Server 派生出一个对所有这些组合都是最优和高效的单一执行计划——我不在乎你认为什么样的技巧ISNULL可以拉动COALESCE或OR不能拉动它。

    我通常按​​此顺序尝试的解决方案是:

    1. 添加OPTION (RECOMPILE)到查询。是的,您每次都需要支付编译成本,但您将获得正确的计划,因为您提供了所提供的参数及其值。

    2. 使用动态 SQL。现在您将能够根据传递的不同参数缓存多个不同的计划。将此与服务器级设置optimize for ad hoc workloads(来自 Kimberly Tripp 的更多信息here和here)相结合,以便仅完全缓存多次使用的计划版本。例子:

      DECLARE
        @PERSON_ID         INT = 0,
        @ITEM_ID           INT = 0,
        @ITEM_VERSION      INT = 1,
        @ITEM_SUB_NAME     VARCHAR(250),
        @ITEM_SUB_SUB_NAME VARCHAR(250);
      
      DECLARE @sql NVARCHAR(MAX) = N'',
      
      SET @sql = N'SELECT ID, PERSON_ID, 
         ISNULL(ITEM_VERSION, 1) AS ITEM_VERSION,
         ISNULL(ITEM_SUB_NAME, '''') AS ITEM_SUB_NAME,
         ISNULL(ITEM_SUB_SUB_NAME, '''') AS ITEM_SUB_SUB_NAME,
         ISNULL(ITEM_DATE, ''19000101'') AS ITEM_DATE
      FROM dbo.PERSON_TBL AS s WITH (NOLOCK)     
      WHERE ITEM_VERSION ' 
      + CASE WHEN @ITEM_VERSION <> 1 THEN 
         N' = @ITEM_VERSION' ELSE N' IS NULL' END
      + CASE WHEN @PERSON_ID <> 0 THEN 
         N' AND PERSON_ID = @PERSON_ID' ELSE N'' END
      + CASE WHEN @EMPLOYEE_ID <> 0 THEN
         N' AND EMPLOYEE_ID = @EMPLOYEE_ID' ELSE N'' END
      + CASE WHEN @ITEM_SUB_NAME IS NOT NULL THEN
         N' AND ITEM_SUB_NAME = @ITEM_SUB_NAME' ELSE N'' END
      + CASE WHEN @ITEM_SUB_SUB_NAME IS NOT NULL THEN
         N' AND ITEM_SUB_SUB_NAME = @ITEM_SUB_SUB_NAME' ELSE N'' END
      ORDER BY PERSON_ID, ITEM_SUB_NAME;';
      
      EXEC sys.sp_executesql @sql,
        N'@PERSON_ID INT, @ITEM_ID INT, @ITEM_VERSION INT,
          @ITEM_SUB_NAME VARCHAR(250), @ITEM_SUB_SUB_NAME VARCHAR(250)',
        @PERSON_ID, @ITEM_ID, @ITEM_VERSION, 
        @ITEM_SUB_NAME, @ITEM_SUB_SUB_NAME;
      

    如果您发现参数嗅探仍然是一个问题,即使您对提供的每种参数组合都有单独的计划(由于基于实际值的基数大不相同),您也可以OPTION (RECOMPILE)在动态 SQL 中添加 - 您再次支付编译成本,但当你有一个更简单的 where 子句时应该会更好。

    有关此模式的一些资源:

    • “厨房水槽”程序(视频)
    • #BackToBasics:更新的厨房水槽示例

    Paul White 也有一篇很棒的文章值得一读:

    • 参数嗅探、嵌入和重新编译选项

    作为旁白:

    • 停止使用单引号作为别名分隔符。它使它们看起来像字符串,并且在某些情况下被弃用,更不用说这里甚至不需要分隔符。(我在这里谈论这个问题。)
    • 停止使用模棱两可的区域日期格式,例如m/d/yyyy(或者是d/m/yyyy?)。
    • 始终使用架构前缀。
    • NOLOCK用作魔术涡轮按钮时要非常小心。
    • 我对COALESCEvs.ISNULL的看法
    • 9

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve