WHERE 中的 CASE 子句
CASE clause in WHERE
我正在做一道 leetcode 题。得出了2个相似的答案。但是我不知道为什么一个是错的,另一个是对的。下面是问题link。目标是编写一个 SQL 查询来查找至少连续出现三次的所有数字。
https://leetcode.com/problems/consecutive-numbers/
table长得像
| Id | Num |
|----|-----|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
正确版本:
select distinct Num as ConsecutiveNums
from Logs, (select @prev := -1, @count := 0) as Init
where (@count := case when @prev = (@prev := Num) then @count + 1 else 1 end) >= 3
输出:
| ConsecutiveNums |
|-----------------|
| 1 |
版本错误:
select distinct Num as ConsecutiveNums
from Logs, (select @prev := -1, @count := 0) as Init
where (case when @prev = (@prev := Num) then @count := @count + 1 else @count := 1 end) >= 3
输出:
| ConsecutiveNums |
|-----------------|
| 1 |
| 2 |
唯一的区别是@count := 移到了 case end 中。
好像是else部分导致了一些错误,据我所知无法解释。
因为您没有将“>= 3”与计数进行比较。这就是我们要针对 3 个连续数字进行测试的内容。在错误的版本中,不确定为什么会有“= - 10”。 "wrong"版本修正后的版本为:
SELECT num AS ConsecutiveNums
FROM logs,
(SELECT @prev := -1,
@count := 0) AS Init
WHERE ( @count := CASE
WHEN @prev = ( @prev := num ) THEN @count := @count + 1
ELSE @count
end ) >= 3
我还添加了一个不同的测试用例以确保:
{"headers": {"Logs": ["Id", "Num"]}, "rows": {"Logs": [[1, 2], [2, 1], [3, 1], [4, 6], [5, 2], [6, 2], [7, 2]]}}
默认一个不改测试用例给出“1”这个测试用例应该return“2”
如果连续意味着 ID 递增“1”(如示例数据中所示),则不需要变量——甚至不需要变量:
select t1.num
from t t1 join
t t2
on t2.id = t1.id + 1 and t2.num = t1.num join
t t3
on t3.id = t1.id + 2 and t3.num = t1.num;
如果您有三个以上的连续值或匹配多行的 num
,那么您可能需要 select distinct num
.
代码的第二个版本由于一个相当晦涩的原因而无法工作。这部分:
else @count := 1
... 有一个没有动态组件的表达式。 MySql 优化了它的执行计划,它不会第二次执行该分配,而只是 returns @count
的当前值。这是因为 MySql 变量实际上并非设计为在 查询执行期间被修改 。当您仍然决定使用该副作用时,您必须意识到这种 "optimisation" 行为。
您可以尝试强制 MySql 每次 进行分配 。这可以通过在分配的表达式中包含变量或字段引用来完成。例如,您可以使用 := if(@count, 1, 1)
而不仅仅是 := 1
。结果是一样的(一直是1),但是现在每次遇到都会重新求值赋值:
where (case when @prev = (@prev := Num)
then @count := @count + 1
else @count := if(@count, 1, 1)
end) >= 3
你可以想出其他的替代表达方式,比如:= 1+Num*0
,只要有对某些variable/field的引用,就可以解决问题。
查看您提供的查询的第一个版本,您会发现分配给 @count
的表达式已经具有此类动态内容。
总而言之,不建议在查询中设置变量,MySql 的未来版本可能不再支持它,如 Reference Manual:
中所述
Previous releases of MySQL made it possible to assign a value to a user variable in statements other than SET
. This functionality is supported in MySQL 8.0 for backward compatibility but is subject to removal in a future release of MySQL.
DECLARE @max_id INTEGER
SET @max_id = (SELECT COUNT(*) FROM seat)
SELECT CASE WHEN id < @max_id THEN case when id%2<>0 then id+1 when id%2=0 then id-1 end
WHEN id = @max_id THEN case when id%2=0 then id-1 else id
END id
,student
FROM seat
ORDER BY id
SET @max_id = (SELECT max(id) FROM seat)
SELECT
案件
当 id!=@max_id 然后
案件
当 id%2!=0 然后 id+1
当 id%2=0 那么 id-1
结尾
当 id= @max_id 然后
案例
当 id%2=0 那么 id-1
当 id%2!=0 那么 id
结尾
结束编号
, 学生
从座位
按 ID 排序`
我正在做一道 leetcode 题。得出了2个相似的答案。但是我不知道为什么一个是错的,另一个是对的。下面是问题link。目标是编写一个 SQL 查询来查找至少连续出现三次的所有数字。
https://leetcode.com/problems/consecutive-numbers/
table长得像
| Id | Num |
|----|-----|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
正确版本:
select distinct Num as ConsecutiveNums
from Logs, (select @prev := -1, @count := 0) as Init
where (@count := case when @prev = (@prev := Num) then @count + 1 else 1 end) >= 3
输出:
| ConsecutiveNums |
|-----------------|
| 1 |
版本错误:
select distinct Num as ConsecutiveNums
from Logs, (select @prev := -1, @count := 0) as Init
where (case when @prev = (@prev := Num) then @count := @count + 1 else @count := 1 end) >= 3
输出:
| ConsecutiveNums |
|-----------------|
| 1 |
| 2 |
唯一的区别是@count := 移到了 case end 中。
好像是else部分导致了一些错误,据我所知无法解释。
因为您没有将“>= 3”与计数进行比较。这就是我们要针对 3 个连续数字进行测试的内容。在错误的版本中,不确定为什么会有“= - 10”。 "wrong"版本修正后的版本为:
SELECT num AS ConsecutiveNums
FROM logs,
(SELECT @prev := -1,
@count := 0) AS Init
WHERE ( @count := CASE
WHEN @prev = ( @prev := num ) THEN @count := @count + 1
ELSE @count
end ) >= 3
我还添加了一个不同的测试用例以确保:
{"headers": {"Logs": ["Id", "Num"]}, "rows": {"Logs": [[1, 2], [2, 1], [3, 1], [4, 6], [5, 2], [6, 2], [7, 2]]}}
默认一个不改测试用例给出“1”这个测试用例应该return“2”
如果连续意味着 ID 递增“1”(如示例数据中所示),则不需要变量——甚至不需要变量:
select t1.num
from t t1 join
t t2
on t2.id = t1.id + 1 and t2.num = t1.num join
t t3
on t3.id = t1.id + 2 and t3.num = t1.num;
如果您有三个以上的连续值或匹配多行的 num
,那么您可能需要 select distinct num
.
代码的第二个版本由于一个相当晦涩的原因而无法工作。这部分:
else @count := 1
... 有一个没有动态组件的表达式。 MySql 优化了它的执行计划,它不会第二次执行该分配,而只是 returns @count
的当前值。这是因为 MySql 变量实际上并非设计为在 查询执行期间被修改 。当您仍然决定使用该副作用时,您必须意识到这种 "optimisation" 行为。
您可以尝试强制 MySql 每次 进行分配 。这可以通过在分配的表达式中包含变量或字段引用来完成。例如,您可以使用 := if(@count, 1, 1)
而不仅仅是 := 1
。结果是一样的(一直是1),但是现在每次遇到都会重新求值赋值:
where (case when @prev = (@prev := Num)
then @count := @count + 1
else @count := if(@count, 1, 1)
end) >= 3
你可以想出其他的替代表达方式,比如:= 1+Num*0
,只要有对某些variable/field的引用,就可以解决问题。
查看您提供的查询的第一个版本,您会发现分配给 @count
的表达式已经具有此类动态内容。
总而言之,不建议在查询中设置变量,MySql 的未来版本可能不再支持它,如 Reference Manual:
中所述Previous releases of MySQL made it possible to assign a value to a user variable in statements other than
SET
. This functionality is supported in MySQL 8.0 for backward compatibility but is subject to removal in a future release of MySQL.
DECLARE @max_id INTEGER
SET @max_id = (SELECT COUNT(*) FROM seat)
SELECT CASE WHEN id < @max_id THEN case when id%2<>0 then id+1 when id%2=0 then id-1 end
WHEN id = @max_id THEN case when id%2=0 then id-1 else id
END id
,student
FROM seat
ORDER BY id
SET @max_id = (SELECT max(id) FROM seat)
SELECT
案件
当 id!=@max_id 然后
案件
当 id%2!=0 然后 id+1
当 id%2=0 那么 id-1
结尾
当 id= @max_id 然后
案例
当 id%2=0 那么 id-1
当 id%2!=0 那么 id
结尾
结束编号
, 学生
从座位
按 ID 排序`