Oracle - 如何模拟位列和布尔AND/OR?
Oracle - 如何模拟位列和布尔AND/OR?
我来自MS SQL和MySQL。这些数据库管理系统提供了一种位数据类型,其中两个布尔值由0和1表示。
我现在参与了一个对我来说是新的Oracle项目。它(某个地方)建议将NUMBER(1)用作位 - 因此值0和1将会存在,但它还支持值-9到-1和2到9。
我们必须在两个这样的列之间进行OR操作。
我还阅读了这篇文章。但是,对于OR(和AND)功能来说,什么是最好的选择,既少写样板代码,又易读(对程序员来说)和高效?
这是一些用于检查的测试代码:
--删除test_bool_number表; 创建表test_bool_number( test_id NUMBER GENERATED BY DEFAULT AS IDENTITY, abool NUMBER(1), bbool NUMBER(1), cbool NUMBER(1) PRIMARY KEY(test_id) ); INSERT INTO test_bool_number(abool,bbool,cbool) select 0, 0, null from dual union all select 0, 1, null from dual union all select 1, 0, null from dual union all select 1, 1, null from dual; SELECT * FROM test_bool_number ORDER BY test_id; -- 查询结果
团队成员建议使用:
-- 选项A + 表示OR,* 表示AND UPDATE test_bool_number SET cbool = abool + bbool;
我发现这个版本可能有效,如果cbool在Java类布尔属性中进行反序列化,但它并不总是有效:
- abool和bbool只能是0或1
- 结果cbool不满足上述条件,如果cbool > 0,则为true
- 如果像这样总结10个以上的布尔列,结果为10,在number(1)中会导致ORA-01438错误
然后我发现了这个:
-- 选项B greatest表示OR,least表示AND UPDATE test_bool_number SET cbool = greatest(abool, bbool);
上述方法很好用,因为结果始终为0或1,但仅限于:
- abool和bbool只能是0或1
- 对于10个或更多列进行OR操作也适用。对于AND操作,可以使用least方法。
但是,如果有人以第一种方式或其他方式计算中间布尔结果 - abool和bbool的值也可以是-9至-1或2至9!
假设所有非0值都代表true(例如C编程语言中的表示方式)
获取布尔值OR的最简单代码是什么?
-- 在true的情况下,bool输入值并不总是只有1! UPDATE test_bool_number SET abool = abool * -5, bbool = bbool * +5; -- 选项C 标准SQL始终正确 UPDATE test_bool_number SET cbool = case when ((abool <> 0) or (bbool <> 0)) then 1 else 0 end;
上述语句完全正确,但有很多样板代码。
所以我找到了另一个选择:
-- 选项D ABS和SIGN UPDATE test_bool_number SET cbool = SIGN(ABS(abool) + ABS(bbool));
它更短,始终正确。即使abool = 9和bbool = 7,并且中间结果为SIGN(16),其中16超出了NUMBER(1)的范围,但它是否高效?
是否有更简单的方法,甚至是高效的方法来处理布尔值?
还是更好地在Oracle表列中表示布尔值的方式是NUMBER(1)以0 = false和1(或其他)= true?
-- 选项E ABS + > 0 UPDATE test_bool_number SET cbool = case when ((ABS(abool) + ABS(bbool)) > 0) then 1 else 0 end;
那么使用二进制或位OR如何?
-- 选项F 用于OR的BITOR UPDATE test_bool_number SET cbool = BITOR(abool, bbool) <> 0 then 1 else 0 end;
上述方法仅适用于OR,无法使用BITAND实现AND功能。
只是一些神奇的想法,对于下一个代码阅读者来说并不容易理解:
-- 选项G 通过文本 UPDATE test_bool_number SET cbool = ABS(SIGN(TO_NUMBER(abool || bbool)));
最内部的部分可以理解为布尔或,就像在许多编程语言(C#,Java等)中一样,但在Oracle中它是一个字符串连接运算符!
所以我们得到了'00','10'等。
最后它只返回0或1,但仅当(abool和)bbool是正值或0时!
而且我怀疑性能(数字转换为文本,文本再转换为数字)。
那么你在Oracle中如何处理布尔值,什么方法最适合能够构建一个可理解的OR(和AND)代码来处理2个或更多其他布尔列,并且性能相当好?
你是否使用其他列类型来表示布尔值?字符?'0'和'1'或'y'和'n'?对于2...n列,你如何进行OR(和AND)操作?
Oracle数据库中没有直接实现位列(bit column)或布尔AND/OR操作的内置功能。然而,可以使用BITAND和BITOR函数来模拟位列和布尔AND/OR操作,并在相关的列上添加CHECK约束来实现。
以下是一个示例代码,展示了如何使用BITAND和BITOR函数以及CHECK约束来模拟位列和布尔AND/OR操作:
SELECT abool, bbool, BITAND(abool, bbool), BITOR(abool, bbool) FROM test_bool_number ORDER BY test_id;
上述代码中的test_bool_number表包含了两个列(abool和bbool),它们都被限制为只能取0或1的值。通过在这两列上添加CHECK约束,可以确保它们只能取合法的值。
在Oracle 11及以上版本中,可以使用BITAND函数来模拟位与操作,而在Oracle 21中,可以使用BITOR函数来模拟位或操作。需要注意的是,BITOR函数在文档中并未明确提及,但实际上是可用的。如果在Oracle 21之前的版本中需要使用BITOR函数,可以创建一个用户自定义函数来实现。
在执行上述代码后,将得到以下输出:
| ABOOL | BBOOL | BITAND(ABOOL,BBOOL) | BITOR(ABOOL,BBOOL) |
|-------|-------|--------------------|-------------------|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 1 |
通过BITAND和BITOR函数,我们可以模拟位列和布尔AND/OR操作,得到期望的结果。
需要注意的是,虽然BITAND函数在文档中有明确的说明,但BITOR函数并未在文档中找到。此外,BITXOR函数也未在文档中找到,但它也是可用的。在Oracle 21中,还引入了许多新的二进制函数,包括BIT_AND_AGG、BIT_OR_AGG和BIT_XOR_AGG,它们都在文档中有明确的说明。此外,BITOR和BITXOR函数也被引入了,但似乎未在文档中记录。
总结起来,使用BITAND和BITOR函数以及CHECK约束可以在Oracle数据库中模拟位列和布尔AND/OR操作。但与SQL Server或MySQL不同,PostgreSQL实际上在数据库中实现了真正的布尔类型,这更加清晰和直观。