use*_*871 5 sql sql-server sql-server-2008
我有以下查询:
SELECT
CASE
WHEN 'Sara' like '% %' THEN SUBSTRING('Sara', 1, CHARINDEX(' ', 'Sara') - 1)
ELSE 'Sara'
END AS FirstName,
CASE
WHEN 'Sara' like '% %' THEN SUBSTRING('Sara', CHARINDEX(' ', 'Sara') + 1, 8000)
ELSE ''
END AS LastName
Run Code Online (Sandbox Code Playgroud)
非常简单 - 我正在测试名称拆分查询.所以我测试名称没有空格的场景,我得到以下异常:
传递给
SUBSTRING函数的长度参数无效.
这是为什么?如果不评估第一个条款并立即进入ELSE?我该怎么解决这个问题?
Y.B*_*.B. 11
优化器足够聪明,可以注意到你在那里有持续表达并尝试评估它.将常量传递给变量会欺骗它运行:
DECLARE @TestString nvarchar(100) = 'Sara';
SELECT
CASE
WHEN @TestString like '% %'
THEN SUBSTRING(@TestString, 1, CHARINDEX(' ', @TestString) - 1)
ELSE @TestString
END AS FirstName,
CASE
WHEN @TestString like '% %'
THEN SUBSTRING(@TestString, CHARINDEX(' ', @TestString) + 1, 8000)
ELSE ''
END AS LastName
Run Code Online (Sandbox Code Playgroud)
要回答这个问题,处理器THEN只有在WHEN为真时才计算表达式,否则只计算ELSE表达式.但即便在此之前,Optimizer会尝试用计算值替换所有常量表达式,以便Processor不必为每一行重新计算它们.它被称为" 恒定折叠 ".
使用变量工作;
DECLARE @NameString varchar(10); SET @NameString = 'Sara'
SELECT
CASE
WHEN @NameString like '% %' THEN SUBSTRING(@NameString, 1, CHARINDEX(' ', @NameString) - 1)
ELSE @NameString
END AS FirstName,
CASE
WHEN @NameString like '% %' THEN SUBSTRING(@NameString, CHARINDEX(' ', @NameString) + 1, 8000)
ELSE ''
END AS LastName
Run Code Online (Sandbox Code Playgroud)
代码的问题在于它会在传递静态值时检查每个部分是否有效.它不喜欢CHARINDEX(' ', 'Sara') - 1解析为-1.解决这个问题的方法是将此函数包装在ABS()函数中;
SELECT
CASE
WHEN 'Sara' like '% %' THEN SUBSTRING('Sara', 1, ABS(CHARINDEX(' ', 'Sara') - 1))
ELSE 'Sara'
END AS FirstName,
CASE
WHEN 'Sara' like '% %' THEN SUBSTRING('Sara', CHARINDEX(' ', 'Sara') + 1, 8000)
ELSE ''
END AS LastName
Run Code Online (Sandbox Code Playgroud)