https://www.tiyee.net/post/178.html
獲得MySQL中不同位的數(shù)量
SELECT BIT_COUNT( CONV( '001011', 2, 10 ) ^ CONV( '001111', 2, 10 ) )
SELECT BIT_COUNT( CONV( '001011', 2, 10 ) ^ CONV( '001111', 2, 10 ) )
https://blog.csdn.net/hhj13978064496/article/details/90379886
SELECT data_code,data_value,bin(data_value) as 二進(jìn)制 FROM tb_ceshi_data where data_value & 8
截止目前我們已經(jīng)學(xué)習(xí)了MySQL的三種運(yùn)算符:算術(shù)運(yùn)算符、比較運(yùn)算符、邏輯運(yùn)算符。三者有一個(gè)共同點(diǎn)都是針對(duì)字符型、表達(dá)式和十進(jìn)制的數(shù)字。
那有沒有一種專門為二進(jìn)制數(shù)字提供的運(yùn)算符呢?這就是本問題的主題:位運(yùn)算符。
先看看位運(yùn)算符的定義:
位運(yùn)算符用來對(duì)二進(jìn)制字節(jié)中的位進(jìn)行位移或者測(cè)試處理,MySQL中提供的位運(yùn)算符有按位或(|)、按位與(&)、按位異或(^)、按位左移(<<)、按位右移(>>)、按位取反(~)等運(yùn)算符。
接下來就通過案例的方式來逐一解開這六類位運(yùn)算符的什么面紗。
【1】按位或:|
select 3|8,4|7|10;
如何從十進(jìn)制的角度看待位運(yùn)算的結(jié)果是無法解釋的。所以我們先要把參與操作的數(shù)字轉(zhuǎn)化為二進(jìn)制形式。如下:
【結(jié)論】
3|8就是兩個(gè)二進(jìn)制數(shù)0011與1000進(jìn)行按位或的計(jì)算,對(duì)應(yīng)的二進(jìn)制位有一個(gè)或兩個(gè)為1,運(yùn)算結(jié)果為1,否則為0。所以,3|8的結(jié)果是1011,轉(zhuǎn)化為十進(jìn)制就是11。同理4|7得到0111,再與1010進(jìn)行按位或得到1111,即為十進(jìn)制數(shù)字15。
【2】按位與:&
select 3&8,4&7&10;
規(guī)則:進(jìn)行按位與的計(jì)算,對(duì)應(yīng)的二進(jìn)制位有兩個(gè)為1,運(yùn)算結(jié)果為1,否則為0。
所以我們可以自己得出結(jié)果:3&8 = 0,4&7&10 = 0。
【3】按位異或:^
select 38,47^10;
規(guī)則:進(jìn)行按位異或的計(jì)算,對(duì)應(yīng)的二進(jìn)制位不相同時(shí),運(yùn)算結(jié)果為1,否則為0。
所以我們可以自己得出結(jié)果:3^8 = 11,4710 = 9。
【4】按位左移:<<
select 8<<2,10<<3;
規(guī)則:進(jìn)行按位左移的功能是讓指定二進(jìn)制的所有位都左移指定的位數(shù)。并且在左移指定位數(shù)后,左邊高位的數(shù)值被移出丟棄,右邊地位空出的位置則用0補(bǔ)齊。
8<<2就是二進(jìn)制數(shù)1000向左移動(dòng)2位得到100000,即為十進(jìn)制數(shù)32;10<<3就是二進(jìn)制數(shù)1010向左移動(dòng)3位得到1010000,即為十進(jìn)制數(shù)80。
【5】按位右移:>>
select 8>>2,10>>3;
規(guī)則:進(jìn)行按位右移與按位左移邏輯一致,只是移動(dòng)的方向變成了右邊,即丟地右邊的位數(shù)。
8>>2變成了10,即為十進(jìn)制數(shù)2;10>>3變成了1,即為十進(jìn)制數(shù)1。
【6】按位取反:~
select 8,3&8;
規(guī)則:按位取反是針對(duì)一個(gè)十進(jìn)制數(shù)對(duì)應(yīng)的二進(jìn)制位上的數(shù)字都進(jìn)行取反操作,即0變成1,1變成0,之后再轉(zhuǎn)化為十進(jìn)制得到結(jié)果。
8得到的是:18446744073709551607;3&8則是18446744073709551607轉(zhuǎn)化為二進(jìn)制后與0011進(jìn)行按位與計(jì)算,結(jié)果為3??偨Y(jié)
位運(yùn)算符是針對(duì)二進(jìn)制字節(jié)的操作;位運(yùn)算符有六種類型;學(xué)習(xí)位運(yùn)算符之前,必須要掌握十進(jìn)制與二進(jìn)制之間的互相轉(zhuǎn)換;針對(duì)兩個(gè)操作數(shù)的有:按位或、按位與、按位異或;針對(duì)一個(gè)操作數(shù)的有:按位左移、按位右移、按位取反。
& : 按位與,二進(jìn)制位同時(shí)都為1的位設(shè)為1。
| : 按位或,二進(jìn)制位有一個(gè)位為1就為1.
^ : 按位異或,對(duì)應(yīng)位的二進(jìn)制數(shù)不同時(shí),對(duì)應(yīng)位的結(jié)果才為1;如果兩個(gè)對(duì)應(yīng)位數(shù)都為0或者都為1,則對(duì)應(yīng)位的結(jié)果為0。
原理
b = 3 轉(zhuǎn)化為2進(jìn)制為 11
b即是 110 與 11
將b中都為1的位設(shè)為1,位數(shù)不夠的補(bǔ)0.即110 與 011
運(yùn)算結(jié)果010,轉(zhuǎn)化為十進(jìn)制結(jié)果為2
應(yīng)用場(chǎng)景
每個(gè)景點(diǎn)包含很多屬性,例如適合旅游的月份,我們一般的做法可能有兩種:
是增加一個(gè)varchar字段,每個(gè)月份之間用一個(gè)特殊符號(hào)分隔保存,例如:"1,2,22,65,7"
建立一個(gè)關(guān)系表,在這里不能使用1-12的數(shù)字來表示月份,而是使用1,2,4,8,16,32,64,128,512,1024,2048,4096來表示,如果是多個(gè)月份,可以相互組合相加,之后存儲(chǔ)為一個(gè)值。
比如 1,10,12月份,就可以存儲(chǔ)1+512+4096=4609,4096 這個(gè)值。
這個(gè)技巧適用于屬性較少的一對(duì)多的場(chǎng)景,可以存儲(chǔ)1個(gè)或者多個(gè),太多的話還是推薦試用關(guān)系表。常用的屬性有:月份,消息提醒類型,各種有限的類型組合等等。
使用技巧:
-- 添加一個(gè)分類 用 “|”
SELECT (4|2|1); --- = 7
-- 去掉一個(gè)分類,用“^”
SELECT 7 ^ 1;
-- 當(dāng)我們需要查詢某個(gè)月份的景點(diǎn)時(shí),例如查詢3月份的景點(diǎn),可使用以下語句:
SELECT * FROM spots WHERE month & 4 = 4;
-- 當(dāng)設(shè)置某個(gè)景點(diǎn)適合某個(gè)月份時(shí),例如設(shè)置4325的景點(diǎn)適合2月份,可使用下面的語句:
UPDATE spots SET month = month | 2 WHERE id = 4325
-- 當(dāng)取消設(shè)置某個(gè)景點(diǎn)的月份時(shí),可使用下面的語句:
UPDATE spots SETmonth = month ^ 2 WHEREid= 4325
-- 查詢同時(shí)適合多個(gè)月份的數(shù)據(jù),例如需要查詢?cè)O(shè)置了11,12,1月份的景點(diǎn),將其三個(gè)月份對(duì)應(yīng)的數(shù)值加起來,結(jié)果為6145,然后使用這個(gè)數(shù)值進(jìn)行查詢:
SELECT * FROM spots WHERE month & 6145 = 6145
-- 查詢只要適合,1,11,12月份其中一個(gè)月份的景點(diǎn)就行
SELECT * FROM spots WHERE (month & 4096 = 4096) or (month & 2048 = 2048) or (month & 1 = 1)