我正在寻找一种类型,根据正常的 bool 类型,NULL(也根据现有的实现,Codd的“A值”或适用的未知)和NA(Codd的“I值”)同时具有TRUE和FALSE “或不适用)。
示例:假设您有一组针对患者的医学测试作为相关变量中的属性,其中患者作为元组。尚未取得的测试结果的值为 NULL(“我们还不知道”),但女性患者永远不会测试睾丸癌,因此该值应为 NA(“不适用于该患者”) 。
我目前正在通过 ENUM 自定义类型实现它(3VL,因为我假设 NULL 将在不声明的情况下继续工作):
CREATE TYPE triplebool AS ENUM ('true', 'false', 'na');
但是,它需要将现有 bool 值转换为文本,然后转换为三元 bool 值:
SELECT has_taken_test::text::triplebool FROM test_record
这既不优雅,也意味着我必须使用文本值('true'、'false'、'na'),而不是 Postgres 的 true bool 类型(TRUE、t、1;等等)所提供的灵活性。所有标准操作)。
我想要的是看起来像(伪代码)的东西:
CREATE TYPE triplebool AS (Boolean || 'na')
以及定义包含“na”值的真值表的能力。
最佳答案
您不能以这种方式扩展(或创建新的扩展类型)boolean
或任何其他标量类型。但你有两种选择:
<强>1。创建一个枚举
并增强它。
就像您尝试的那样,您可以创建一个枚举
(排序很重要:它将决定如何按order by
、索引等排序)。
create type triplebool as enum ('false', 'na', 'true');
您可以通过defining some custom ones使强制转换变得更容易:
-- sample casts: from and to boolean
create function bool2triplebool(boolean)
returns triplebool
immutable
strict
language sql
as $func$
select case $1
when false then 'false'::triplebool
when true then 'true'::triplebool
end
$func$;
create function triplebool2bool(triplebool)
returns boolean
immutable
strict
language sql
as $func$
select case $1
when 'false' then false
when 'true' then true
end
$func$;
-- use implicit instead of assignment, if you want to
-- use this cast automatically in any expression,
-- not just in column assigments (in INSERT & UPDATE statement)
create cast (boolean as triplebool)
with function bool2triplebool(boolean)
as assignment;
-- this should be explicit (default)
create cast (triplebool as boolean)
with function triplebool2bool(triplebool);
如果您愿意,您可以模拟一些boolean
的标准操作。您可以创建自定义 functions & operators实现这一目标:
-- sample operator: and
create function triplebool_and(triplebool, triplebool)
returns triplebool
immutable
called on null input -- important, if you want to use null as the 4th "value"
language sql
as $func$
select res
from (values (null::triplebool, null::triplebool, null::triplebool),
(null, 'false', null ),
(null, 'na', 'na' ),
(null, 'true', null ),
('false', null, null ),
('false', 'false', 'false'),
('false', 'na', 'na' ),
('false', 'true', 'false'),
('na', null, 'na' ),
('na', 'false', 'na' ),
('na', 'na', 'na' ),
('na', 'true', 'na' ),
('true', null, null ),
('true', 'false', 'false'),
('true', 'na', 'na' ),
('true', 'true', 'true' )) t(lop, rop, res)
where lop is not distinct from $1
and rop is not distinct from $2
-- "is [not] distinct from" can handle null values too
$func$;
create operator && (
leftarg = triplebool,
rightarg = triplebool,
procedure = triplebool_and,
commutator = && -- for joins
);
但是,正如您所看到的,要实现 boolean
类型的几乎所有功能将需要大量工作。它有一个严重的限制:你不能改变 enum
类型的输入和/或输出函数(至少,以可靠的方式;你可以开始搞乱 pg_type
,但我不确定会发生什么&您的更改可能无法导出/转储等)。这意味着,您只能使用定义的值(false
、na
和 true
)进行输入,不能使用别名 (如 f
、1
和 boolean
)可以使用(从 text
进行转换是一个完全不同的故事,甚至可能与类型的 IO 函数不一致)。
<强>2。 Create真正的user-defined type .
使用此选项,您可以创建真正的 4VL bool 类型,但它会带来一定的成本,即它比 enum
选项需要更多工作。这些类型通常用 C(或与 PostgreSQL 源代码有适当绑定(bind)的语言)编写。您不能在 sql
中编写这些低级 IO 函数,也不能在 plpgsql
中编写。
+1。重新设计您的表架构(一点点)
虽然上述解决方案可以发挥作用,但它们会使您的数据无法移植。另外,如果您需要另一列,哪个值取决于这些行是否适用(如测试日期),这些新列是否也需要 n/a 状态?在我看来,不是。该数据应该位于不同的列中:
create table med_test (
-- ...
is_applicable boolean not null,
result boolean,
taken_at timestamp with time zone
);
如果您查看上面此表的一行,您可以清楚地区分您的案例:
is_applicable is false
表示不适用,is_applicable 为 true,结果为 null
表示尚未获取
您可以选择添加完整性检查,以避免不适用,但已采取状态:
alter table med_test
add check (is_applicable is true or result is null);
alter table med_test
add check (is_applicable is true or taken_at is null);
关于postgresql - 如何在 PostgreSQL 中创建 4VL "boolean"类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28232168/