为了为Oracle SQL中的一个非常特殊的用例节省保存空间,我正在尝试使用位图方法来表示一段时间内的布尔状态(当天发生或未发生的事件),二进制数中的每个位表示当天的是/否。这样,例如,一个32位的数字将允许我表示连续32天每天是否发生了一些事情。我只需要计算设置(=1)的位数,就可以得到事件发生的32天内的天数,而不必为每一天存储单独的日期行。
下面是我每天测试更新值的一个例子(滚动最旧的位并设置最新的位):
SELECT testbitmap original_bitmap,
BITAND((testbitmap * POWER(2,/*rolloff the nbr of days since last event*/1)) + /*the event happened today*/1,POWER(2,32)-1) new_bitmap
FROM (SELECT BIN_TO_NUM(1,1,0,0,1,1,0,0,0,1,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0) testbitmap
FROM dual)
到目前为止还不错。但是现在我需要查询结果,这意味着计算结果位图的设置位数。据我所知,在Oracle中没有BIN_TO_NUM
的逆。如果不使用循环遍历每个位并单独测试的函数,是否有方法在Oracle中计算设置位(例如,数字9的结果应该是2(1001
),而数字7的结果应该是3(0111
)。也许是一个数学公式,它会返回表示二进制数字所需的1的个数?
3条答案
按热度按时间brc7rcf01#
你可以使用Hamming Weight algorithm,在C++中它是:
在Oracle中,您可以使用以下函数:
那么计算你的价值将是:
其输出:
| ORIGINAL_BITMAP| NUMBER_OF_SET_BITS32(测试位图)|
| --|--|
| 3429605376 | 11 |
你也应该能够使用Java创建一个Oracle函数:
对于相同的
SELECT
查询,它给出相同的输出。64位和128位数字:
如果你想要一个64位(或128位)的函数,那么将所有十六进制字符串的宽度加倍(四倍),并将最后的除法从224改为256(2120),以获得最高的8位,正如在这个答案顶部链接的答案中提到的那样:
或者,在Java中,对于更大的数字,可以使用
java.math.BigInteger.bitCount()
:fiddle
cwdobuhd2#
我不像@MT0那么聪明,我会使用这种方法。首先将数字转换为二进制:
然后你可以用REGEXP_COUNT来计算
1
我认为汉明权重算法会更有效。您可以考虑将值存储为
RAW
,而不是整数值。UTL_RAW
软件包提供了各种功能,应该可以满足您的需求。dsf9zpds3#
这是另一个想法,从计数的数量设置位在一个32位整数这是很容易理解的。用PL/SQL写的,它很简单:
测试输出:
虽然这确实会循环,但它不会对每一位都循环...仅针对每个设置位(1)。因此,一个具有稀疏位(几个1)的数字将循环很少的次数。这使得它很适合用于跟踪稀疏事件。
然而,在大数字的实际实现中,特别是在MT 0增强了Hamming Weight算法以支持128位数字之后,我发现Hamming Weight方法仍然明显更快。