这个CASE表达式如何达到ELSE子句?

时间:2014-02-20 20:14:39

标签: sql sql-server-2008 tsql

我需要将一些测试数据加载到Account表的Channel字段中。频道可以是10个不同值中的一个,所以我想我会使用CASE表达式和ABS(CHECKSUM(NewId())) % 10随机分配频道中的一个值,如下所示:

SELECT 
    id,
    name,
    Channel = 
      CASE ABS(CHECKSUM(NewId())) % 10
        WHEN 0 THEN 'Baby Only'
        WHEN 1 THEN 'Club'
        WHEN 2 THEN 'Drug'
        WHEN 3 THEN 'Food'
        WHEN 4 THEN 'Internet'
        WHEN 5 THEN 'Liquidators'
        WHEN 6 THEN 'Mass'
        WHEN 7 THEN 'Military'
        WHEN 8 THEN 'Other'
        WHEN 9 THEN 'Speciality'
        ELSE '*NONE*'            -- How is this ever getting reached?
      END
FROM 
    retailshelf_nil...account A

由于我使用modulo 10,我认为唯一可能的值应该是0-9。但是当我运行上面的代码时,我发现确实正在达到ELSE子句,并且我的数据在某些记录中显示为'NONE',如下所示:

id                  name    Channel
001L000000KpgFqIAJ  Acct1   *NONE*
001L000000KpgFrIAJ  Acct2   Mass
001L000000KpgFsIAJ  Acct3   Club
001L000000KpgFtIAJ  Acct4   *NONE*
001L000000KpgFuIAJ  Acct5   Baby Only
001L000000KpgFvIAJ  Acct6   *NONE*
001L000000KpgFwIAJ  Acct7   Mass

有人可以解释一下我允许ELSE条款到达的逻辑错误吗?

当我运行一个简单的测试来生成如下的随机数时:

SELECT
    RadomNum = ABS(CHECKSUM(NewId())) % 10
FROM 
    retailshelf_nil...account A
ORDER BY 
    1

生成的所有数字都是0-9,正如预期的那样,那么第一个SQL有什么不同?

是否有确保永远不会达到ELSE的解决方法?

3 个答案:

答案 0 :(得分:44)

查询的书面形式扩展为:

Channel = 
      CASE
        WHEN ABS(CHECKSUM(NewId())) % 10 = 0 THEN 'Baby Only'
        WHEN ABS(CHECKSUM(NewId())) % 10 = 1 THEN 'Club'
        WHEN ABS(CHECKSUM(NewId())) % 10 = 2 THEN 'Drug'
        WHEN ABS(CHECKSUM(NewId())) % 10 = 3 THEN 'Food'
        WHEN ABS(CHECKSUM(NewId())) % 10 = 4 THEN 'Internet'
        WHEN ABS(CHECKSUM(NewId())) % 10 = 5 THEN 'Liquidators'
        WHEN ABS(CHECKSUM(NewId())) % 10 = 6 THEN 'Mass'
        WHEN ABS(CHECKSUM(NewId())) % 10 = 7 THEN 'Military'
        WHEN ABS(CHECKSUM(NewId())) % 10 = 8 THEN 'Other'
        WHEN ABS(CHECKSUM(NewId())) % 10 = 9 THEN 'Speciality'
        ELSE '*NONE*'            -- How is this ever getting reached?
      END

每次测试都会使用NEWID的新值。

答案 1 :(得分:5)

将为每个WHEN子句计算一个新的“随机”数字 - 您可以改为使用派生表:

SELECT ID, Name, 
    Channel = 
      CASE Rand
        WHEN 0 THEN 'Baby Only'
        WHEN 1 THEN 'Club'
        WHEN 2 THEN 'Drug'
        WHEN 3 THEN 'Food'
        WHEN 4 THEN 'Internet'
        WHEN 5 THEN 'Liquidators'
        WHEN 6 THEN 'Mass'
        WHEN 7 THEN 'Military'
        WHEN 8 THEN 'Other'
        WHEN 9 THEN 'Speciality'
        ELSE '*NONE*'            -- How is this ever getting reached?
      END
FROM
(   SELECT 
         id,
         name,
         ABS(CHECKSUM(NewId())) % 10 Rand
    FROM   
        retailshelf_nil...account A
) zzz;

或CROSS APPLY子查询:

SELECT A.ID, A.Name, 
    Channel = 
      CASE zzz.Rand
        WHEN 0 THEN 'Baby Only'
        WHEN 1 THEN 'Club'
        WHEN 2 THEN 'Drug'
        WHEN 3 THEN 'Food'
        WHEN 4 THEN 'Internet'
        WHEN 5 THEN 'Liquidators'
        WHEN 6 THEN 'Mass'
        WHEN 7 THEN 'Military'
        WHEN 8 THEN 'Other'
        WHEN 9 THEN 'Speciality'
        ELSE '*NONE*'            -- How is this ever getting reached?
      END
FROM
    retailshelf_nil...account A
CROSS APPLY
(   SELECT 
        ABS(CHECKSUM(NewId())) % 10
) zzz (Rand);

这样每条记录只调用一次NewID()

类似的痤疮已解决here

T-SQL documentation解释了这种现象(授予它适用于Sybase,但显然仍然适用于SQL Server):

  

引用rand函数,getdate函数的表达式,   等等,每次评估时都会产生不同的值。这个   在某些情况下使用这些表达式时会产生意外结果   案例表达。例如,SQL标准指定了这种情况   表达式:

case expression
    when value1 then result1
    when value2 then result2
    when value3 then result3
...
end
     

等同于以下形式的case表达式:

case expression
    when expression=value1 then result1
    when expression=value2 then result2
    when expression=value3 then result3
...
end

答案 2 :(得分:0)

与第二个问题相关,

CHECKSUM(NewId())有时会返回否定结果,这与任何case条件都不匹配。如果负数除以任何数字,结果将为负数。执行以下查询

declare @v nvarchar(50) = newid()
select CHECKSUM(@v),@v,CHECKSUM(@v) % 10