【问题标题】:How to optimize a query with multiple OR, AND, IN statements?如何优化具有多个 OR、AND、IN 语句的查询?
【发布时间】:2020-01-06 02:44:03
【问题描述】:

我有一个如下的 SQL 查询:

    Declare @ConnectionType int = 5, 
    @UserId int = 2

    select * from CallDetails 
    Where ((@ConnectionType = 0 AND CallDetails.DeviceType IN (0,1,2,3,4,5,7,8))
            OR (@ConnectionType = 1 AND CallDetails.DeviceType = 4)
            OR (@ConnectionType = 2 AND CallDetails.DeviceType IN (0,1,2,3,7))
            OR (@ConnectionType = 3 AND CallDetails.DeviceType = 5)
            OR (@ConnectionType = 4 AND CallDetails.DeviceType IN (0,1,2,3,4,7))
            OR (@ConnectionType = 5 AND CallDetails.DeviceType IN (4,5))
            OR (@ConnectionType = 6 AND CallDetails.DeviceType IN (0,1,2,3,5,7))
            OR (@ConnectionType = 7 AND CallDetails.DeviceType IN (8))
            OR (@ConnectionType = 8 AND CallDetails.DeviceType IN (0,1,2,3,7,8))
            OR (@ConnectionType = 9 AND CallDetails.DeviceType IN (5,8))
            OR (@ConnectionType = 10 AND CallDetails.DeviceType IN (4,8))
            OR (@ConnectionType = 11 AND CallDetails.DeviceType IN (0,1,2,3,4,8))
            OR (@ConnectionType = 12 AND CallDetails.DeviceType IN (0,1,2,3,5,8))
            OR (@ConnectionType = 13 AND CallDetails.DeviceType IN (4,5,8)) 
            OR (@ConnectionType = 14 AND CallDetails.DeviceType IN (0,1,2,3,7,4,5))
            OR @ConnectionType IS NULL)

查询的另一部分是:

            AND (@UserId IS NULL OR @ConnectionType IN (1,3,5,7,9,10,13)

            OR (@ConnectionType = 0 AND (CallDetails.DeviceType IN (4,5,8) OR (CallDetails.UserId = @UserId)))
            OR (@ConnectionType = 2 AND ((CallDetails.UserId = @UserId)))
            OR (@ConnectionType = 4 AND (CallDetails.DeviceType = 4 OR (CallDetails.UserId = @UserId)))
            OR (@ConnectionType = 6 AND (CallDetails.DeviceType = 5 OR (CallDetails.UserId = @UserId)))
            OR (@ConnectionType = 8 AND (CallDetails.DeviceType = 8 OR (CallDetails.UserId = @UserId)))
            OR (@ConnectionType = 11 AND (CallDetails.DeviceType IN (4,8) OR (CallDetails.UserId = @UserId)))
            OR (@ConnectionType = 12 AND (CallDetails.DeviceType IN (5,8) OR (CallDetails.UserId = @UserId))) 
            OR (@ConnectionType = 14 AND (CallDetails.DeviceType IN (4,5) OR (CallDetails.UserId = @UserId)))
        )

@ConnectionType 是多个设备的组合,在此基础上将确定DeviceType。以后将添加任何其他设备@ConnectionType 组合将增加等等。这个查询也在多个Store Procedures 中使用。如何优化此查询?

【问题讨论】:

  • 这似乎使用查找表或使用动态语句会更好(我个人会推荐先验,因为它可能更容易实现)。
  • @Larnu 怎么办?有什么线索吗?

标签: sql sql-server optimization query-optimization


【解决方案1】:

您有几个优化选项: 1. 在 where 子句的列上添加索引,这应该可以加快查询速度 2.创建一个有ConnectionType,DeviceType的帮助表(关联表)并加入它 3.调整你的存储过程并使用动态查询,像这样:

DECLARE @ConnectionType INT = 0,
   @UserId INT = 2,
   @DeviceTypeString NVARCHAR(100) = NULL

IF @ConnectionType = 0
   SET @DeviceTypeString = N'0,1,2,3,4,5,7,8'

IF @ConnectionType = 1
    SET @DeviceTypeString = N'4'

IF @ConnectionType = 2
   SET @DeviceTypeString = N'0,1,2,3,7'

IF @ConnectionType = 3
    SET @DeviceTypeString = N'5'

IF @ConnectionType = 4
    SET @DeviceTypeString = N'0,1,2,3,4,7'

IF @ConnectionType = 5
    SET @DeviceTypeString = N'4,5'

IF @ConnectionType = 6
    SET @DeviceTypeString = N'0,1,2,3,5,7'

IF @ConnectionType = 7
    SET @DeviceTypeString = N'8'

IF @ConnectionType = 8
    SET @DeviceTypeString = N'0,1,2,3,7,8'

EXEC sp_executesql N'SELECT * FROM CallDetails AS cd WHERE (@1 IS NULL OR @1 = @1) AND (cd.DeviceType IN (SELECT [Value] FROM STRING_SPLIT(@2, '','')) OR cd.UserID = @3)',
    N'@1 INT, @2 NVARCHAR(100), @3 INT',
    @ConnectionType,
    @DeviceTypeString,
    @UserID

我不想固执己见,我已经调整了我的示例以使用 sp_executesql ;-)

【讨论】:

  • 从不将参数注入动态语句。使用 sp_executesql 参数化您的语句。
  • 另外,WHERE 1 = 1 除了减慢查询速度之外,还添加了什么?
  • 另外,你不需要所有这些ELSEs;您可以简单地为每个使用 IF,因为它后面跟着一个语句:DB<>fiddle。删除ELSEs 和额外的制表符将使声明更加简洁。
  • @Larnu: WHERE 1 = 1 只是有一个功能性 where 子句的助手,这不是真的必要,但添加其他 where 子句字符串更容易......你是对的最好使用 sp_executesql,但这是一个快速编写的示例,用于展示如何加快速度的另一种选择...
  • 对于 sp_executesql 阅读:blogs.msdn.microsoft.com/turgays/2013/09/17/… 我已经写过这段代码应该在一个存储过程中,所以 sql 注入并不是一个真正的问题,但是正如 Larnu 所写的那样,sp_executesql 是更好的选择
【解决方案2】:

正如我所提到的,看起来您最好使用查找表。你的表可能看起来像这样简单(注意我没有添加索引、外键约束等,但你可能想要/需要它们):

CREATE TABLE dbo.ConnectionLookup (ConnectionType int,
                                   DeviceType int);

INSERT INTO dbo.ConnectionLookup (ConnectionType,
                                  DeviceType)
VALUES(0,0),
      (0,1),
      (0,2),
      (0,3),
      (0,4),
      (0,5),
      (0,7),
      (0,8),
      (1,4),
      ...
      (14,0),
      (14,1),
      (14,2),
      (14,3),
      (14,4),
      (14,5),
      (14,7);

然后,您可以在查找表上执行JOIN

DECLARE @ConnectionType int = 5;
        --@UserId int = 2; --Commented out, as never used.

SELECT {Your Columns}
FROM dbo.CallDetails CD
     JOIN dbo.ConnectionLookup CL On CD.DeviceType = CL.DeviceType
WHERE CL.ConnectionType = @ConnectionType;

然而,这有点猜测,因为缺乏样本数据和预期结果,但应该(但)让您走上正确的道路

【讨论】:

  • 我可以把所有这些逻辑放在一个单独的函数中吗?它会降低性能吗?
  • 把什么逻辑放到一个单独的函数里?
  • 这个查找表是你创建的@Larnu
  • 函数不是表格,@AfnanAhmad;它们是非常不同的对象类型。
  • 这部分查询开始在多个存储过程中使用。我怎样才能集中它?
【解决方案3】:

您可以创建一个包含两列(ConnectionType、DeviceType)的表,其中每个连接类型都有多条记录。然后将通话详细信息与此表相结合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多