странное поведение Where: переменные

romy4
Дата: 08.02.2013 00:35:30
есть таблица слов с хешами

word,hash:

...
вицеспикер, fgeuw5890
вицепрезидент, asjfkl34
Вицин, gfasw1p2
вицеконсул, q3i4pfas
вицеанин, air9020
...



что будет в @a и @b, если сделать:
SET @a=0;
SET @b=0;



вариант 1:

SELECT *
FROM
`words`
WHERE
(`word` LIKE "виц%" AND @a:=@a+1) OR (`hash`='q3i4pfas' AND @b:=@b+1)




вариант 2:

SELECT *
FROM
`words`
WHERE
(`word`="вицеконсул" AND @a:=`word`) OR (`hash`='q3i4pfas' AND @b:=`hash`)




результат:
вариант 1: @a=5,@b=1
вариант 2: @a="вицеконсул",@b="q3i4pfas"

WTF?? зачем выполняется вторая часто OR-а, если найдена первая?

ещё вариант 3: намеренно делаем в первой части ошибку

SELECT *
FROM
`words`
WHERE
(`word`="ошибочное123" AND @a:=`word`) OR (`hash`='q3i4pfas' AND @b:=`hash`)




Будет правильный результат: @a=0,@b="q3i4pfas".
romy4
Дата: 08.02.2013 00:44:47
Кому интересно, вот исходное:
CREATE TABLE `words` (`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`word` VARCHAR(255) NOT NULL,
`hash` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
)ENGINE=MyISAM;

INSERT INTO `words` (`word`,`hash`) VALUES
('вицеспикер', 'fgeuw5890'),
('вицепрезидент', 'asjfkl34'),
('Вицин', 'gfasw1p2'),
('вицеконсул', 'q3i4pfas'),
('вицеанин', 'air9020');
Stupid_BOT
Дата: 08.02.2013 03:22:30
romy4,
в первом варианте у меня возвращается четыре записи и получается @a=4, @b=0.
+
mysql> show global variables like 'character\_set\_%';
+--------------------------+--------+
| Variable_name | Value |
+--------------------------+--------+
| character_set_client | cp1251 |
| character_set_connection | cp1251 |
| character_set_database | cp1251 |
| character_set_filesystem | binary |
| character_set_results | cp1251 |
| character_set_server | cp1251 |
| character_set_system | utf8 |
+--------------------------+--------+
7 rows in set (0.00 sec)

mysql> set @a=0, @b=0;
Query OK, 0 rows affected (0.02 sec)

mysql> SELECT *,@a,@b
-> FROM
-> `words`
-> WHERE
-> (`word` LIKE "виц%" AND @a:=@a+1) OR (`hash`='q3i4pfas' AND @b:=@b+1);
+----+---------------+-----------+------+------+
| id | word | hash | @a | @b |
+----+---------------+-----------+------+------+
| 1 | вицеспикер | fgeuw5890 | 1 | 0 |
| 2 | вицепрезидент | asjfkl34 | 2 | 0 |
| 4 | вицеконсул | q3i4pfas | 3 | 0 |
| 5 | вицеанин | air9020 | 4 | 0 |
+----+---------------+-----------+------+------+
4 rows in set (0.00 sec)

Во втором варианте возвращается пустой набор записей. Следовательно первая часть OR не является истиной ни для одной записи, потому и вычисляется вторая часть. Может поможет осознать "почему так"
select 1=1 and @a:='вицеконсул' BOOL_A, @a;
javajdbc
Дата: 08.02.2013 05:29:01
romy4,

1. сделайте себе одолжение, НИКОГДА не используйте
переменные в WHERE блоке.

2. на всеких пожарный, поставьте дополнительные
скобки, чтоб не вспоминать какие преимушества
у операторов LIKE = AND OR :=
miksoft
Дата: 08.02.2013 08:19:58
javajdbc
1. сделайте себе одолжение, НИКОГДА не используйте
переменные в WHERE блоке.
Я бы сформулировал не столь категорично - не используйте переменные в WHERE блоке, которые изменяются в этом же запросе.
romy4
Дата: 08.02.2013 13:51:17
javajdbc,

Расставив скобки вокруг действий во втором примере
((`word`="вицеконсул") AND (@a:=`word`)) OR ((`hash`='q3i4pfas') AND (@b:=`hash`))

результат не поменялся

в where мне нужны переменные, без нельзя. В примере не полный вариант всей выборки, там в зависимости от сработавшего поля `word` или `hash` должно выполняться ещё одно условие.

WHERE
((`word`="ошибочное123" AND @a:=1) OR (`hash`='q3i4pfas' AND @a:=2)) AND (IF(@a<2,check_smth(`word`),check_other(`hash`))


конечно, можно всё вынести в отдельную функцию. Поведение не критическое, но во всяком случаее нелогичное для mysql
romy4
Дата: 08.02.2013 14:09:45
Stupid_BOT,

да, вы совершенно правы; я вчера затупил уже вечером

(`word`="вицеконсул" AND NOT ISNULL(@a:=`word`)) OR (`hash`='q3i4pfas' AND NOT ISNULL(@b:=`hash`))


выполняется коректно. Всё дело в том, что возвращает присвоение переменной и приведение её результата к булу. Оно равняется нулю как оказалось.

SELECT CAST((@a:='вицеконсул') AS SIGNED) s,@a;


спасибо.