我正在寻找一个根据普通布尔类型同时具有TRUE和FALSE的类型,NULL(同样根据现有实现,Codd'" A-Value"或者适用的未知)和NA(Codd' I-Value"或不适用)。
示例:假设您有一组患者的医学检查作为relvar中的属性,患者为元组。尚未采取的测试结果是值为NULL("我们还不知道")但是女性患者将永远不会测试睾丸癌,因此该值应该是NA(& #34;不适用于该患者")。
我目前正在通过ENUM自定义类型实现它(3VL,因为我假设NULL将继续工作而不被声明):
CREATE TYPE triplebool AS ENUM ('true', 'false', 'na');
但是,它需要将现有的布尔值转换为text,然后转换为triplebool:
SELECT has_taken_test::text::triplebool FROM test_record
这既不优雅,又意味着我必须使用文字值(' true',' false',' na')而不是由Postgres的' true布尔类型(TRUE,t,1;等等+所有标准操作)。
我喜欢的东西看起来像(伪代码):
CREATE TYPE triplebool AS (Boolean || 'na')
以及定义包含' na'的真值表的能力。值。
答案 0 :(得分:2)
您不能以这种方式扩展(或创建新的扩展类型的)boolean
或任何其他标量类型。但是你有两种选择:
<强> 1。创建enum
&amp;加强它。
就像你尝试过的那样,你可以创建一个enum
(排序很重要:它将决定如何按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&amp; 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
&amp; true
)进行输入,不能使用别名(例如f
,可以使用1
与boolean
)(从text
转换为完全不同的故事,甚至可能与某种类型的IO函数不一致。)
<强> 2。 Create真正的user-defined type。
使用此选项,您可以创建一个真正的4VL布尔类型,但它带有成本,它将比enum
选项更加有效。这些类型通常用C
编写(或者用一种语言编写,它与PostgreSQL的源代码有适当的绑定)。您无法在sql
和plpgsql
中编写这些低级IO功能。
<强> 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 is true and result is 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);