【问题标题】:Force a short circuit on a CASE statement强制 CASE 语句短路
【发布时间】:2016-06-17 14:14:20
【问题描述】:

我有一个存储过程,我正在尝试使用它来提高它的效率。不幸的是,它目前可能被许多应用程序使用,所以尽管我的最佳解决方案是重写它,但我目前受到那里的限制。

最初有两个输入参数,都是 VARCHAR。我需要根据第一个参数的值将两者与不同的数据类型进行比较。但是,当它们不应该存在时,我会遇到转换错误。示例:

    PROCEDURE ProcedureName
        @SearchType VARCHAR(24)
        , @SearchID VARCHAR(100)
    AS

    BEGIN  
      SELECT  
        t1.*,  
        t2.*  
      FROM  
        TableOne t1  
        JOIN TableTwo t2  
      WHERE  
            (CASE  
                WHEN UPPER(@SearchType) = 'GUID' THEN CAST(t2.guid AS VARCHAR(100))  
                WHEN UPPER(@SearchType) = 'ID' THEN t2.id  
                WHEN UPPER(@SearchType) = 'ANOTHERID' THEN t1.id  
            END)  
            =  
            (CASE  
                WHEN UPPER(@SearchType) = 'GUID' THEN @SearchID  
                WHEN UPPER(@SearchType) = 'ID'  
                    OR UPPER(@SearchType) ='ANOTHERID' THEN CAST(@SearchID AS INT)  
            END)
    END

这里的目标是如果 Search 类型是 GUID,它将转换表中的 guid 列并与输入字符串进行比较。 由于数据库中的两个 ID 字段都是 int 类型的列,我想将 SearchID(当传入 ID 或 ANOTHERID 时将具有整数的基础数据类型)转换为整数类型以匹配本机列类型。 但是,每次我尝试使用@SearchType 中的“GUID”和@SearchID 中的 guid 运行它时,都会收到此错误: 将 varchar 值“”转换为数据类型 int 时转换失败。

这个 case 语句不应该是短路的,所以这个输入不应该转换为 int 吗?可以强制短路吗?

【问题讨论】:

  • where子句中删除所有计算和转换,声明三个适当类型的变量,在select语句之前分配它们并使用准备比较where中的变量。并输入option(recompile),因为存储的计划不会在下次运行时让您感到舒适。
  • 我在过程中添加了以下内容:IF UPPER(@SearchType) = 'ID' OR UPPER(@SearchType) ='ANOTHERID' BEGIN SET @IntSearchID = CAST(@SearchID AS INT) END 然后我在最后只使用一个新变量进行比较,因为输入变量已经是一个 varchar,其他两个选项是both int: WHEN UPPER(@SearchType) = 'ID' OR UPPER(@SearchType) ='ANOTHERID' THEN @IntSearchID 但是,我仍然收到转换错误。

标签: sql-server case short-circuiting


【解决方案1】:

问题不在于两个CASE 表达式之间的比较,而在于每个CASE 表达式本身。

CASE  
    WHEN UPPER(@SearchType) = 'GUID' THEN CAST(t2.guid AS VARCHAR(100))  
    WHEN UPPER(@SearchType) = 'ID' THEN t2.id  
    WHEN UPPER(@SearchType) = 'ANOTHERID' THEN t1.id  
END

请记住,在CASE 表达式中,所有返回必须具有相同的数据类型。如果不是,那么它们将被转换为具有更高优先级的数据类型。在上面的CASE 表达式中,您有一个VARCHAR 和两个INT。由于INT 具有更高的data type precedence,因此第一个WHEN 的结果将转换为INT 并产生错误:

将 varchar 值转换为数据类型时转换失败 诠释。

要解决此问题,您可以将其他两个 INT 转换为 VARCHAR

(CASE  
    WHEN UPPER(@SearchType) = 'GUID' THEN CAST(t2.guid AS VARCHAR(100))  
    WHEN UPPER(@SearchType) = 'ID' THEN CAST(t2.id AS VARCHAR(100))
    WHEN UPPER(@SearchType) = 'ANOTHERID' THEN CAST(t1.id AS VARCHAR(100))
END)
=
(CASE  
    WHEN UPPER(@SearchType) = 'GUID' THEN @SearchID  
    WHEN UPPER(@SearchType) = 'ID'  
        OR UPPER(@SearchType) ='ANOTHERID' THEN @SearchID
END)

另一种选择是结合使用ANDOR 条件:

WHERE
    (UPPER(@SearchType) = 'GUID' AND CAST(t2.guid AS VARCHAR(100)) = @SearchID)
    OR (UPPER(@SearchType) = 'ID' AND t2.id = CAST(@SearchID AS INT))
    OR (UPPER(@SearchType) = 'ANOTHERID' AND t1.id = CAST(@SearchID AS INT))

【讨论】:

  • 感谢您的回复!
  • 我以为输入会将我带到下一行,但它却发布了呵呵。第一个建议基本上是已经存在的。我的 DBA 担心的是,由于这两个表非常大,因此需要从它们的本机数据类型进行大量转换,这可能会减慢查询速度。目标是通过仅对输入变量运行计算来最小化转换。我无法让第二个建议起作用;它给了我同样的错误。
猜你喜欢
  • 1970-01-01
  • 2021-08-20
  • 1970-01-01
  • 2021-05-26
  • 2013-02-15
  • 2017-08-23
  • 2018-09-05
  • 2014-08-18
  • 1970-01-01
相关资源
最近更新 更多