2014-02-20 6 views
17

Мне нужно загрузить некоторые тестовые данные в поле «Канал» в моей учетной записи. Канал может быть один из 10 различных значений, так что я думал, что я случайно назначить канал одного из значений, используя выражение CASE вместе с ABS(CHECKSUM(NewId())) % 10 так:Как это выражение CASE достигает предложения ELSE?

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 

Поскольку я использую по модулю 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 так и не был достигнут?

+2

Когда 'ABS (CHECKSUM (NewId()))' возвращает NULL, ELSE будет исполнять. Это произойдет, когда NewId() вернет NULL. – DwB

+2

@ DwB, спасибо за ваш ответ, но почему NEWID() когда-нибудь вернет NULL? – PaulStock

+0

Если вы просто ищете случайное значение, и вы получаете значения, выходящие иначе, чем другие, почему бы просто не перевернуться еще на одну из ваших других ценностей, так что иначе было бы «Specialty», а также 9, уверен, что это может испортить но это не имеет значения. – Phaeze

ответ

43

Письменная форма запроса расширяется до:

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 используется в каждом тесте.

+2

Это объясняет это – Lamak

+0

Я знал, что это должно быть что-то фундаментальное, связанное с тем, как работает оператор CASE', который я испортил. – PaulStock

+0

Ну, это довольно откровение, но ... почему? (Ничего, я просто риторический.) –

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 ОТНОСИТЬСЯ подзапрос:

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() является вызывается только один раз за запись.

похожее scneario было решено here.

T-SQL documentation объясняет это явление (предоставивший это для Sybase, но очевидно, все еще относится к SQL Server):

выражения, которые ссылаются на функцию rand, функцию getdate, и так далее, получены различные значения каждый раз, когда они оцениваются. Этот может дать неожиданные результаты при использовании этих выражений в определенных выражениях case .Например, стандарт SQL определяет, что случай выражения с формой:

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

эквивалентен следующим видом прецедентного выражения:

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

Это неправда. Это относится только к RAND(), который обрабатывается как числовая константа. Даже если бы был только один вызов NEWID(), он получил бы то же значение. То, что пользователь испытывает, - это значение не в (0 .. 9). –

+1

@ CRAFTYDBA: И какое значение это будет? Вы пытались запустить этот CASE? –

+1

@CRAFTYDBA Я реплицировал результаты и подтвердил исправление - найденная документация указывает, что она применяется к _any_ операции, а не только к RAND(). –

0

Связанных ко второму вопросу,

CHECKSUM (NewId()) иногда возвращает отрицательные результаты, которые не соответствуют ни одному из условий case. Если отрицательное число делится на любое число, результат будет отрицательным. Выполните следующий запрос:

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

Именно поэтому я использовал 'ABS', чтобы гарантировать, что результат' CHECKSUM' всегда был положительным. – PaulStock

+0

Но вы еще не закончили свой второй вопрос. Поэтому я добавил этот ответ, чтобы он был полезен. – Bharadwaj

 Смежные вопросы

  • Нет связанных вопросов^_^