【问题标题】:CASE clause in WHEREWHERE 中的 CASE 子句
【发布时间】:2018-12-02 04:10:23
【问题描述】:

我正在做一个 leetcode 问题。得出了2个类似的答案。但我不知道为什么一个是错的,另一个是对的。以下是问题链接。目标是编写一个 SQL 查询来查找所有至少连续出现 3 次的数字。

https://leetcode.com/problems/consecutive-numbers/

桌子的样子

| 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 部分导致了一些错误,我的知识无法解释。

【问题讨论】:

  • 您需要提供一些示例数据,其中查询失败并在问题中成功,而不是通过链接。
  • “但我不知道为什么”:这令人惊讶,因为第二个与 -10 相比.....似乎微不足道?
  • 无论如何,两者都不正确,因为它们都假设记录将按Num的顺序访问,这并不能保证。
  • 没有样本数据或结果,这个问题不清楚。该链接指向一个带有 SQL Server 示例的站点,但问题显然是关于 MySQL 的。
  • 感谢您帮助我。我刚刚更新了问题,使其格式良好。 @戈登

标签: mysql sql case-when


【解决方案1】:

代码的第二个版本由于一个相当模糊的原因不能工作。这部分:

else @count :=  1

... 有一个没有动态组件的表达式。 MySql 优化其执行计划的方式是它不会再次执行该分配,而只是返回@count 的当前值。这是因为 MySql 变量实际上并非设计为在执行查询时修改。当您仍然决定使用该副作用时,您必须意识到这种“优化”行为。

您可以尝试强制 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,只要有对某个变量/字段的引用,就可以解决问题。

查看您提供的查询的第一个版本,您会看到分配给@count 的表达式已经具有这样的动态内容。

总而言之,不建议在查询中设置变量,MySql 的未来版本可能不再支持它,如Reference Manual 所述:

以前的 MySQL 版本可以在 SET 以外的语句中为用户变量赋值。 MySQL 8.0 支持此功能以实现向后兼容性,但在 MySQL 的未来版本中可能会被删除。

【讨论】:

  • 谢谢!这是我急切想知道的。
【解决方案2】:

因为您没有将 ">= 3" 与计数进行比较。这就是我们要测试的 3 个连续数字。在错误的版本中,不确定为什么会有“= - 10”。 “错误”版本的更正版本是:

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”这个测试用例应该返回“2”

【讨论】:

  • 谢谢。我刚刚更新了我的问题,使其可读。我的错。现在我测试了正确和错误的版本,得到了上述结果。希望你能再次帮助我。
【解决方案3】:
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) 选择 案子 当 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 结尾 结束编号 , 学生 来自座位
ORDER BY id`

【讨论】:

    【解决方案4】:

    如果连续意味着 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

    【讨论】:

    • 为什么会被否决?它解决了 OP 想要解决的问题。
    • 谢谢。我理解这个解决方案,但现在我想知道为什么用户定义变量的位置可能会导致意外结果。我不知道谁投了反对票,但似乎答案与我的问题无关。 @戈登
    • @昆州 . . .如果可以避免使用变量,您真的不想使用它们。这回答了您提出的问题:“目标是编写一个 SQL 查询来查找所有至少连续出现 3 次的数字。”
    猜你喜欢
    • 1970-01-01
    • 2014-09-08
    • 2020-03-26
    • 2017-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-24
    • 2021-08-09
    相关资源
    最近更新 更多