二进制比较给出了错误的结果MYSQL

时间:2015-03-06 12:32:43

标签: php mysql sql

将此DB称为Employees:

+--+----------------+
|ID|Ability_required|
+--+----------------+
|1 |   0000000111   |
|2 |   0000001111   |
|3 |   0000000111   |
|4 |   0000001101   |
|5 |   0000001111   |
+--+----------------+

这个SQL查询:

SELECT ID FROM `Employees`
WHERE `Ability_required` & b'0000001111' = b'0000001111'

为什么我的MYSQL会返回第1行和第3行?我在这里做错了什么?

检查图片以获取示例: enter image description here

enter image description here

3 个答案:

答案 0 :(得分:4)

您的Especialidad_Nails列似乎是一个字符串数据类型,其中包含01 字符的字符串。

尝试对这些字符串执行数字运算(例如按位AND)将首先导致MySQL将这些字符串转换为数字:'0000000111'被强制转换为十进制111,即b'1101111' -it是这个值然后经历按位运算(并且成功!)。

如果确实想要使用位字段,则应考虑MySQL的SET数据类型 - 它是作为表面下的位字段实现的(实际上,您可以使用按位运算)正如你试图做的那样),但也通过提供一个人性化的名称来公开一个更易于访问的界面,这个名称可以非常有效地用于FIND_IN_SET()等操作。

也就是说,位操作不是可以理解的,并且位字段不承认关系数据库提供的许​​多优点。有时最好将每个位存储在自己的BOOLEAN列中。

如果您别无选择,只能将这些位存储为字符串类型列中的字符,则可以(如@SalmanA points out)使用MySQL的CONV()函数转换为二进制文件。

答案 1 :(得分:3)

bitwise and对整数进行操作时,您将数字存储为字符串。 MySQL将执行隐式转换,但是,它假定字符串表示十进制数。

使用MySQL CONV函数手动转换字符串:

SELECT * FROM `Employees`
WHERE CONV(`Ability_required`, 2, 10) & b'0000001111' = b'0000001111';
ID Ability_required
2  0000001111
5  0000001111

我建议使用INT字段。或者如果可能,将表格标准化。

答案 2 :(得分:1)

您的ability_required列的数据类型有误。你有它们作为VARCHAR(10),它们需要是整数类型或位(x)类型。 B' 001111'只看起来像一个字符串,但它实际上是二进制表示的整数。

b'001111' = 15
b'000111' = 7
b'001101' = 13

如果您将desired_ability的列更改为INT或BIT(8)或TINYINT,那么您的查询将按预期工作。

要测试结果,请尝试

SELECT ID, (`Ability_required` & b'0000001111') FROM `Employees` WHERE `Ability_required` & b'0000001111' = b'0000001111'

你会看到你得到的不是bit_和你期望的。

这是我的测试:

CREATE TABLE `employees` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ability` int(11) DEFAULT '0',
  `b_ability` bit(8) DEFAULT NULL,
  `v_ability` VARCHAR(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

delete from employees;

insert into employees (ability, b_ability, v_ability) values (7,  b'000111', '000111');
insert into employees (ability, b_ability, v_ability) values (15, b'001111', '001111');
insert into employees (ability, b_ability, v_ability) values (7,  b'000111', '000111');
insert into employees (ability, b_ability, v_ability) values (13, b'001101', '001101');
insert into employees (ability, b_ability, v_ability) values (15, b'001111', '001111');

select *, (b'001111' & v_ability) from employees where v_ability & b'001111' = b'001111';

如果您已经拥有要保留在这些列中的数据,您可以使用函数转换它们(mysql不是我的强项,可能效率不高但函数有效):

CREATE FUNCTION `vcharbit_to_int`(bitdata varchar(16)) RETURNS int(11)
    DETERMINISTIC
BEGIN
     declare i INT DEFAULT length(bitdata);
     declare v INT default 0;
     declare b VARCHAR(1);
     declare ip INT default 1;

     while i > 0 do
        set b := SUBSTR(bitdata, i, 1);
        if b = '1' then 
            set v := v + ip;
        END IF;
        set i := i - 1;
        set ip := ip * 2;
     end while;

     RETURN v;
END

查询:

select id, v_ability from employees where vcharbit_to_int(v_ability) & b'001111' = b'001111';

会为您提供您期望的结果。