2013-08-25 3 views
2

Я пытался ответить другим SO question и неожиданно столкнулся со следующей проблемой. Баллы должны быть назначены на 3 наивысших оценки (mrk) групп (grp) каждого класса (sec). Самые высокие баллы получают 5 очков, второй рейтинг - 3 очка, а группы в 3-м ранге - всего 1 очко. Для всех остальных pts должно быть установлено значение null.Как использовать пользовательские переменные в инструкции UPDATE?

| ID | SEC | GRP | MRK | PTS | 
|----|-----|-----|-----|--------| 
| 1 | cl2 | ge | 32 | (null) | 
| 2 | cl1 | gb | 22 | (null) | 
| 3 | cl1 | gd | 22 | (null) | 
| 4 | cl1 | ge | 18 | (null) | 
| 5 | cl2 | ga | 26 | (null) | 
| 6 | cl1 | ga | 55 | (null) | 
| 7 | cl2 | gb | 66 | (null) | 
| 8 | cl2 | gc | 15 | (null) | 
| 9 | cl1 | gc | 12 | (null) | 
| 10 | cl2 | gf | 5 | (null) | 
| 11 | cl2 | ge | 66 | (null) | 

Я выбрал для работы с пользовательскими переменными, поскольку они обеспечивают максимальную гибкость в отношении схемы распределения и вскоре пришли к следующему решению:

SELECT id,sec,grp,mrk, 
CASE WHEN @s=sec THEN   -- whenever there is a new class ... 
CASE WHEN @m=mrk THEN @i ELSE -- issue the same points for 
           -- identical scorers, otherwise ... 
    CASE WHEN IF(@m:=mrk,@i,@i)>2 THEN @i:[email protected] -- store mrk in @mrk and 
           -- while @i>2 return points: 3 or 1 ... 
           ELSE @i:=null -- no points for the rest 
    END 
END 
ELSE NULLIF(@i:=5,(@s:=sec)=(@m:=mrk)) -- store sec in @s and mrk in @m 
             -- and return points: 5 
END pts 
FROM tbl ORDER BY sec,mrk desc 

Объяснение в NULLIF(@i:=5,(@s:=sec)=(@m:=mrk)):

выражения @s:=sec и @m:=mrk оцениваются, а затем их значения сравниваются =. Результат может быть либо 0 (false), либо 1 (true), но он определенно будет не равным 5, другому аргументу функции NULLIF, поэтому в конце будет возвращен только первый аргумент (5). Я выбрал конструкцию, чтобы сделать два назначения переменных, не возвращая ничего.

ОК, может быть, не самое простое решение ;-), но я сделал обратить внимание определить каждую переменную только один раз для каждой записи, которая обрабатывается, так как «порядок оценки для выражений, включающих пользовательские переменные не определен "mysql manual. select действительно дает мне нужную

результат:

| ID | SEC | GRP | MRK | PTS | 
|----|-----|-----|-----|--------| 
| 6 | cl1 | ga | 55 |  5 | 
| 2 | cl1 | gb | 22 |  3 | 
| 3 | cl1 | gd | 22 |  3 | 
| 4 | cl1 | ge | 18 |  1 | 
| 9 | cl1 | gc | 12 | (null) | 
| 7 | cl2 | gb | 66 |  5 | 
| 11 | cl2 | ge | 66 |  5 | 
| 1 | cl2 | ge | 32 |  3 | 
| 5 | cl2 | ga | 26 |  1 | 
| 8 | cl2 | gc | 15 | (null) | 
| 10 | cl2 | gf | 5 | (null) | 

Теперь мой вопрос:

Как написать UPDATE заявления по той же схеме, что будет хранить выше вычисленные результаты в столбец pts?

Мои попытки до сих пор все не удалось:

UPDATE tbl SET pts= 
CASE WHEN @s=sec THEN 
CASE WHEN @m=mrk THEN @i ELSE 
    CASE WHEN IF(@m:=mrk,@i,@i)>2 THEN @i:[email protected] 
           ELSE @i:=null 
    END 
END 
ELSE NULLIF(@i:=5,(@s:=sec)=(@m:=mrk)) 
END 
ORDER BY sec,mrk desc 

результат:

| ID | SEC | GRP | MRK | PTS | 
|----|-----|-----|-----|-----| 
| 6 | cl1 | ga | 55 | 5 | 
| 2 | cl1 | gb | 22 | 5 | 
| 3 | cl1 | gd | 22 | 5 | 
| 4 | cl1 | ge | 18 | 5 | 
| 9 | cl1 | gc | 12 | 5 | 
| 7 | cl2 | gb | 66 | 5 | 
| 11 | cl2 | ge | 66 | 5 | 
| 1 | cl2 | ge | 32 | 5 | 
| 5 | cl2 | ga | 26 | 5 | 
| 8 | cl2 | gc | 15 | 5 | 
| 10 | cl2 | gf | 5 | 5 | 

Почему оператор обновления получить только одно значение (5) для очков?!?

Вы можете найти все данные и инструкции SQL в моем SQLfiddle.

+0

что пожелаете обновить результат. –

+0

@echo_Me мой желаемый результат UPDATE указан в моем сообщении в разделе «Выбор действительно дает мне желаемый результат:». FYI: Я также попытался вставить вывод SELECT в (временную) таблицу -> тот же результат, что и в нижней части моего сообщения :-( – cars10m

ответ

1

Я попытался отладить этот случай.
Я добавил 6 новых столбцов в таблице tbl: B_S, b_m, B_i и A_S, A_m, a_i
b_ * - означает "до", а_ * - означает "после",
и я изменил запрос к:

UPDATE tbl SET 
    b_s = @s, 
    b_m = @m, 
    b_i = @i, 
pts= 
CASE WHEN @s=sec THEN 
CASE WHEN @m=mrk THEN @i ELSE 
    CASE WHEN IF(@m:=mrk,@i,@i)>2 THEN @i:[email protected] 
           ELSE @i:=null 
    END 
END 
ELSE NULLIF(@i:=5,(@s:=sec)=(@m:=mrk)) 
END, 
a_s = @s, 
a_m = @m, 
a_i = @i 
ORDER BY sec,mrk desc 

Мое намерение состояло в регистрации значений переменных до и после оценки выражения.

Это странно - я не знаю почему, но кажется, что когда вы назначаете значения всем переменным перед выполнением обновления, тогда обновление работает должным образом.
Сравните эти две демки:

1 - неправильно: http://sqlfiddle.com/#!2/2db3e4/1
2 - мелкие: http://sqlfiddle.com/#!2/37ff5/1

Единственное отличие заключается в следующем фрагменте кода перед обновлением:

set @i='alamakota'; 
set @m='alamakota'; 
set @s='alamakota'; 

Некоторые вид на " magic string ":)

+0

Интересное наблюдение и хорошая идея с колонками до и после! Я буду играть с скрипка тоже ... – cars10m

+0

Странная вещь: документально ли она где-нибудь?!? Будет ли возможность писать все в одном утверждении (без команд 'SET ...' before)? – cars10m

+0

Ну, возможно, это * тогда ответ: ** Переменные, которые вы хотите использовать в 'UPDATE', должны быть * объявлены * (они также привязаны к типу char) с использованием какой-либо инструкции' SET' раньше. ** ('SET @s: = @ m: = @ i: = 'a'' также работает.) Хотя я нашел (более простой) пример в сообщениях в разделе UPDATE руководства MySQL для переключения столбцов: 'UPDATE tbl SET sec = (@ v : = sec), sec = grp, grp = @ v; '. (Этот оператор работает * без * предыдущего' SET'.) Однако в этом примере переменная '@ v' никогда не тестировалась * (имеет значение проверено) в конструкции 'CASE'. – cars10m

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