SQL Server(特别是 2008 或 2012)CASE语句是评估所有WHEN条件还是在找到WHEN评估为真的子句后退出?如果它确实通过了整个条件集,这是否意味着最后一个条件评估为 true 会覆盖第一个评估为 true 的条件所做的事情?例如:
SELECT
CASE
WHEN 1+1 = 2 THEN'YES'
WHEN 1+1 = 3 THEN 'NO'
WHEN 1+1 = 2 THEN 'NO'
END
Run Code Online (Sandbox Code Playgroud)
结果是“YES”,即使最后一个 when 条件应该使它评估为“NO”。一旦找到第一个 TRUE 条件,它似乎就退出了。有人可以确认是否是这种情况。
让我们检查这两个语句:
IF (CONDITION 1) OR (CONDITION 2)
...
IF (CONDITION 3) AND (CONDITION 4)
...
Run Code Online (Sandbox Code Playgroud)
如果CONDITION 1是TRUE,会CONDITION 2被检查吗?
如果CONDITION 3是FALSE,会CONDITION 4被检查吗?
条件如何WHERE:SQL Server 引擎是否优化WHERE子句中的所有条件?程序员是否应该以正确的顺序放置条件以确保 SQL Server 优化器以正确的方式解析它?
添加:
感谢 Jack 提供链接,来自 t-sql 代码的惊喜:
IF 1/0 = 1 OR 1 = 1
SELECT 'True' AS result
ELSE
SELECT 'False' AS result
IF 1/0 = 1 AND 1 = 0
SELECT 'True' …Run Code Online (Sandbox Code Playgroud) 目的
在尝试创建自引用函数的测试示例时,一个版本失败而另一个版本成功。
唯一的区别是添加SELECT到函数体导致两者的执行计划不同。
起作用的功能
CREATE FUNCTION dbo.test5(@i int)
RETURNS INT
AS
BEGIN
RETURN(
SELECT TOP 1
CASE
WHEN @i = 1 THEN 1
WHEN @i = 2 THEN 2
WHEN @i = 3 THEN dbo.test5(1) + dbo.test5(2)
END
)
END;
Run Code Online (Sandbox Code Playgroud)
调用函数
SELECT dbo.test5(3);
Run Code Online (Sandbox Code Playgroud)
退货
(No column name)
3
Run Code Online (Sandbox Code Playgroud)
不起作用的功能
CREATE FUNCTION dbo.test6(@i int)
RETURNS INT
AS
BEGIN
RETURN(
SELECT TOP 1
CASE
WHEN @i = 1 THEN 1
WHEN @i = 2 THEN 2
WHEN @i = 3 …Run Code Online (Sandbox Code Playgroud) 在尝试编写查询时,我发现(困难的方法)SQL Server 在执行查询时解析 SELECT 之前很久就解析了查询中的 WHERE。
的MSDN文档说,一般逻辑解析顺序是这样的:SELECT被解析几乎最后(因此导致“没有这样的对象[别名]”试图使用在其他条款列别名时误差)。甚至有人建议允许在任何地方使用别名,但被微软团队驳回,理由是 ANSI 标准合规性问题(这表明这种行为是 ANSI 标准的一部分)。
作为一名程序员(不是 DBA),我发现这种行为有些令人困惑,因为在我看来它在很大程度上违背了拥有列别名的目的(或者,至少,如果列别名是在查询执行中更早地解析),因为您可以实际使用别名的唯一地方是在 ORDER BY 中。作为一名程序员,它似乎错过了使查询更强大、更方便和 DRY 的巨大机会。
看起来这是一个如此明显的问题,它有理由认为,除了 SELECT 和 ORDER BY 之外,还有其他原因决定不允许列别名,但这些原因是什么?
我一直都明白,该CASE语句遵循“短路”原则,因为如果先前步骤被评估为真,则不会对后续步骤进行评估。(此答案是否 SQL Server CASE 语句评估所有条件或在第一个 TRUE 条件时退出?相关但似乎并未涵盖这种情况并且与 SQL Server 相关)。
在以下示例中,我希望MAX(amount)根据开始日期和支付日期之间的月份数计算不同月份之间的月份范围。
(这显然是一个构建的示例,但该逻辑在我看到问题的实际代码中具有有效的业务推理)。
如果开始日期和支付日期之间的时间小于 5 个月,则将使用表达式 1,否则将使用表达式 2。
这会导致错误“ORA-01428:参数‘-1’超出范围”,因为 1 条记录的数据条件无效,导致 ORDER BY 的 BETWEEN 子句的开头为负值。
SELECT ref_no,
CASE WHEN MONTHS_BETWEEN(paid_date, start_date) < 5 THEN
-- Expression 1
MAX(amount)
OVER (PARTITION BY ref_no ORDER BY paid_date ASC
ROWS BETWEEN MONTHS_BETWEEN(paid_date, start_date) PRECEDING
AND CURRENT ROW)
ELSE
-- Expression 2
MAX(amount)
OVER (PARTITION BY ref_no ORDER BY paid_date ASC
ROWS …Run Code Online (Sandbox Code Playgroud) 我正在使用 CTE 将 SSRS 存储的 proc 转换为 BO 存储的 proc,因为显然我不能将临时表与 Business Objects 一起使用。
我有这个查询:
;WITH cte1
AS
(
SELECT cv.issue
, cv.customfield
, CAST(STRINGVALUE AS NUMERIC) AS priority_num
--INTO #temp_priority_val
FROM proddb1.customfieldvalue cv WITH (NOLOCK)
INNER JOIN proddb1.customfield e WITH (NOLOCK)
ON cv.CUSTOMFIELD = e.id
AND e.cfname = 'Issue Priority')
,cte2
AS
(
SELECT a.ISSUE
, f.customvalue priority_num
--INTO #temp_priority
FROM cte1 a
INNER JOIN proddb1.customfieldoption f WITH (NOLOCK)
ON a.CUSTOMFIELD = f.CUSTOMFIELD
AND CAST(a.priority_num AS NUMERIC) = f.id)
SELECT …Run Code Online (Sandbox Code Playgroud) 我收到一个非常奇怪的错误。考虑下表:
CREATE TABLE #MyTable (
Key1 INT , Key2 INT ,
x SMALLINT , y INT , z INT ,
a FLOAT , b FLOAT , c SMALLINT , s FLOAT
)
-- insert many records
CREATE UNIQUE CLUSTERED INDEX CI ON #MyTable ( Key1 , Key2 )
Run Code Online (Sandbox Code Playgroud)
出于某种原因,以下更新语句尝试除以零。这只能在c=0或 时发生c=1。该WHERE条款明确规定c>1。
-- this fails with divide-by-zero error
UPDATE #MyTable
SET s = CASE
WHEN a - SQUARE ( b ) / c <= …Run Code Online (Sandbox Code Playgroud)