咱们首先来看一个系统中常见的需求:php
有一个广告表,咱们要对广告作显示控制:sql
可能的表结构以下:数据库
CREATE TABLE `finger_ad` ( `ad_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `ad_name` varchar(50) NOT NULL COMMENT '广告名称', `ad_image_url` varchar(255) NOT NULL COMMENT '广告图片', `ad_url` varchar(255) NOT NULL COMMENT '广告图片URL跳转地址', `is_vip` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否仅限 VIP 显示', `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT '显示状态:1显示、0隐藏', PRIMARY KEY (`ad_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='广告表';
假如后期,咱们需求更改了。须要再增长几种限制:服务器
遇到这种限制条件的需求,开发同窗是否是很伤脑筋?学习
可能不少开发第一反应就是在表结构增长这种新增的限制条件字段。一切看来彷佛很美好。url
的确,这样添加字段是最快最容易的方式。也能完成咱们的需求。spa
可是,这样会引来以下毛病:code
那么,还有没有更好的方式解决这些问题呢?blog
答案:有!图片
这就是咱们今天要讲的按位与运算符的高级技巧。
咱们把上面的表结构改一下:
DROP TABLE IF EXISTS `finger_ad`; CREATE TABLE `finger_ad` ( `ad_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `ad_name` varchar(50) NOT NULL COMMENT '广告名称', `ad_image_url` varchar(255) NOT NULL COMMENT '广告图片', `ad_url` varchar(255) NOT NULL COMMENT '广告图片URL跳转地址', `bit_condition` INT(11) UNSIGNED NOT NULL COMMENT '位运算条件:1-登陆可访问、2-未登陆可访问、4-30天注册可访问、8-30天未登陆可访问、16-未消费可访问、32-VIP可访问', `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT '显示状态:1显示、0隐藏', PRIMARY KEY (`ad_id`) ) ENGINE=InnoDB DEFAULT CHARSET UTF8 COMMENT='广告表';
咱们把全部的条件都去掉了。增长了一个字段: bit_condition 。把全部的条件都组合到一个字段。
那咱们此时该如何写代码呢?
好比,如今要添加以下限制条件的广告:
只容许登陆用户访问或已注册 30 天用户或是 VIP 用户才容许访问该广告。
那么,这个广告的 bit_condition 该如何设置值呢?很简单,把这几个条件的位值直接相加。此时值为:37。
不少可能会很奇怪。设置为 37 ,我怎么知道是这几个值的和呢?若是对 Linux 系统权限熟悉的同窗就很容易理解这种作法。实际上,这里运用了按位与运算的特性:任意组合相加的值不会重复。
这个理解起来有必定难度。我三两句也很难给你梳理明白。你们能够在网上深刻挖掘一下这方面儿的知识。你只须要知道这一点特色便可。
那么,如今咱们该如何写 SQL 呢?
示例以下:
SELECT * FROM finger_ad WHERE display = 1 AND bit_condition & 3 = bit_condition
这条 SQL 语句当中的 3 对应的是当前用户针对这么多条件获得的数值。若是 bit_condition位值是与 3 按位与与 bit_condition 结果相同,说明条件符合。
咱们经过一个字段解决了全部条件的问题。着实得感谢按位与运算符的特性。同时也对 MySQL能支持位运算符感到开心。
那么,它有什么缺点呢?
想必有经验的同窗已经看出来了。这种写法只能知足包含关系。假如要实现同时知足 3 个条件才能访问就不行了。或者,一个知足另一个取反。优势明显,一样缺点也很明显。你们要根据实际状况来选用。
扩展学习文件:
<?php //例子1 echo '<h2>demo1</h2><br>'; //定义常量 define('D1',1); define('D2',2); define('D3',4); define('D4',8); define('D5',16); function showStatus($state){ for($i= 1;$i<=5;$i++){ $d = 'D'.$i; $dd = constant($d); //取对应常量的值 if(($state & $dd) > 0){ echo '第'.$i.'盏灯亮'.'<br>'; //8 }else{ echo '<span >第'.$i.'盏灯灭</span>'.'<br>'; } } echo "sql语句写法:SELECT * FROM `table` WHERE state & {$state} = state;"; echo '<br>'; echo '<br>'; } $state = D4; showStatus($state); //只开第4盏灯 $state = D1; showStatus($state); //只开第1盏灯 $state = D4 | D1; showStatus($state); //开第1盏灯和第4盏灯 $state = (D4 | D2 | D1) & (~D1); showStatus($state); //开第1盏灯,第2盏灯和第4盏灯,而后关闭第1盏灯 //例子2 echo '<h2>demo2</h2><br>'; /** * 一、权限应用 * 拥有哪些权限,就把这些权限对应的数值加起来 * 例如:版主拥有权限(增长、删除、修改、查询),则版主的权限值存储为15(8+4+2+1) * 而后【权限值之和】 与 【实际权限值】作【位于】比较 * 结果是真则拥有权限 * 结果是假则没有权限 * * 注意:权限值必须是2的N次方,从0次方开始,31次方是2147483648 * 32次方是4294967296,已超过了经常使用int(10)最大存储4294967295,因此必须注意权限数量(<31个) * 固然若是存储格式为bitint或varchar等能够存储更长数字的格式,那么权限数量能够继续增长 */ // 赋予权限值-->删除:八、上传:四、写入:二、只读:1 define('mDELETE',8); define('mUPLOAD',4); define('mWRITE',2); define('mREAD',1); //vvvvvvvvvvvvv使用说明vvvvvvvvvvvvv //部门经理的权限为(假设它拥有此部门的全部权限),| 是位或运行符,不熟悉的就查查资料 echo '所有权限为:'.(mDELETE|mUPLOAD|mWRITE|mREAD).'<br>';// 至关因而把上面的权限值加起来:8+4+2+1=15 /* *赋予它多个权限就分别取得权限值相加,又好比某位员工拥有除了删除外的权限其他都拥有,那它的权限值是多少? *应该是:4+2+1=7 */ //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //判断某人的权限可用,设权限值在$key中 /* *判断权限用&位与符, */ // 设我只有 upload 和 read 权限,则 $key = (mUPLOAD|mREAD);//至关因而把上传、只读的权限值分别相加:4+1=5 echo '当前权限为:'.$key.'<br>';; if($key & mDELETE){ echo '有删除权限'.'<br>'; //8 }else{ echo '<span >无删除权限</span>'.'<br>'; //8 } if($key & mUPLOAD){ echo '有上传权限'.'<br>'; //4 } else{ echo '<span >无上传权限</span>'.'<br>'; //4 } if($key & mWRITE){ echo '有写入权限'.'<br>'; //2 } else{ echo '<span >无写入权限</span>'.'<br>'; //2 } if($key & mREAD){ echo '有只读权限'.'<br>'; //1 } else{ echo '<span >无只读权限</span>'.'<br>'; //1 } ?>
运行结果: