如何在 PostgreSQL 中创建 4VL "boolean" 类型?
How do I create a 4VL "boolean" type in PostgreSQL?
我正在寻找一种类型,根据普通布尔类型,NULL(也根据现有实现,Codd 的 "A-Value" 或适用的未知)和 NA(Codd 的 "I-Value" 或不适用)。
示例:假设您有一组患者的医学测试作为 relvar 中的属性,患者作为元组。尚未取得的测试结果值为 NULL ("we don't know yet"),但女性患者永远不会测试睾丸癌,因此该值应为 NA ("not applicable to this patient")。
我目前正在通过 ENUM 自定义类型实现它(3VL,因为我假设 NULL 将在不声明的情况下继续工作):
CREATE TYPE triplebool AS ENUM ('true', 'false', 'na');
但是,它需要将现有的布尔值转换为文本,然后再转换为 triplebool:
SELECT has_taken_test::text::triplebool FROM test_record
这既不优雅,也意味着我必须使用文本值('true'、'false'、'na')而不是 Postgres 的真正布尔类型(TRUE , t, 1; 等等 + 所有标准操作)。
我想要的东西看起来像(伪代码):
CREATE TYPE triplebool AS (Boolean || 'na')
以及定义包含 'na' 值的真值 table 的能力。
您不能以这种方式扩展(或创建新的扩展类型)boolean
或任何其他标量类型。但是你有两个选择:
1.创建一个 enum
并增强它。
就像您尝试的那样,您可以创建一个 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
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
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
and rop is not distinct from
-- "is [not] distinct from" can handle null values too
$func$;
create operator && (
leftarg = triplebool,
rightarg = triplebool,
procedure = triplebool_and,
commutator = && -- for joins
);
但是,如您所见,要实现 boolean
类型的几乎所有功能将需要大量工作。它有一个严重的限制:你不能改变 enum
类型的输入 and/or 输出函数(至少,以可靠的方式;你可以开始搞乱 pg_type
,但我不确定会发生什么,您的更改可能不会 exporttable / dump-able 等)。这意味着,您只能使用定义的值(false
、na
和 true
)作为输入,不能使用 alias(如 f
, 1
和 boolean
) 可以使用(从 text
转换到 text
是完全不同的故事,甚至可能与类型的 IO 函数不一致)。
2。 Create a truly user-defined type.
使用此选项,您可以创建真正的 4VL 布尔类型,但它会带来成本,即它比 enum
选项需要更多工作。这些类型通常用 C
编写(或用一种语言编写,它与 PostgreSQL 的源代码具有适当的绑定)。您不能在 sql
或 plpgsql
.
中编写这些低级 IO 函数
+1。重新设计您的 table 模式(稍微)
虽然上述这些解决方案 可以 工作,但它们会使您的数据变得不可靠 table。此外,如果您需要另一列,哪个值取决于这些行是否适用(如测试日期),这些新列是否需要 n/a状态也是?在我看来,不是。该数据应在不同的列中:
create table med_test (
-- ...
is_applicable boolean not null,
result boolean,
taken_at timestamp with time zone
);
如果你看上面一行table,你可以清楚地分开你的案例:
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);
我正在寻找一种类型,根据普通布尔类型,NULL(也根据现有实现,Codd 的 "A-Value" 或适用的未知)和 NA(Codd 的 "I-Value" 或不适用)。
示例:假设您有一组患者的医学测试作为 relvar 中的属性,患者作为元组。尚未取得的测试结果值为 NULL ("we don't know yet"),但女性患者永远不会测试睾丸癌,因此该值应为 NA ("not applicable to this patient")。
我目前正在通过 ENUM 自定义类型实现它(3VL,因为我假设 NULL 将在不声明的情况下继续工作):
CREATE TYPE triplebool AS ENUM ('true', 'false', 'na');
但是,它需要将现有的布尔值转换为文本,然后再转换为 triplebool:
SELECT has_taken_test::text::triplebool FROM test_record
这既不优雅,也意味着我必须使用文本值('true'、'false'、'na')而不是 Postgres 的真正布尔类型(TRUE , t, 1; 等等 + 所有标准操作)。
我想要的东西看起来像(伪代码):
CREATE TYPE triplebool AS (Boolean || 'na')
以及定义包含 'na' 值的真值 table 的能力。
您不能以这种方式扩展(或创建新的扩展类型)boolean
或任何其他标量类型。但是你有两个选择:
1.创建一个 enum
并增强它。
就像您尝试的那样,您可以创建一个 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
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
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
and rop is not distinct from
-- "is [not] distinct from" can handle null values too
$func$;
create operator && (
leftarg = triplebool,
rightarg = triplebool,
procedure = triplebool_and,
commutator = && -- for joins
);
但是,如您所见,要实现 boolean
类型的几乎所有功能将需要大量工作。它有一个严重的限制:你不能改变 enum
类型的输入 and/or 输出函数(至少,以可靠的方式;你可以开始搞乱 pg_type
,但我不确定会发生什么,您的更改可能不会 exporttable / dump-able 等)。这意味着,您只能使用定义的值(false
、na
和 true
)作为输入,不能使用 alias(如 f
, 1
和 boolean
) 可以使用(从 text
转换到 text
是完全不同的故事,甚至可能与类型的 IO 函数不一致)。
2。 Create a truly user-defined type.
使用此选项,您可以创建真正的 4VL 布尔类型,但它会带来成本,即它比 enum
选项需要更多工作。这些类型通常用 C
编写(或用一种语言编写,它与 PostgreSQL 的源代码具有适当的绑定)。您不能在 sql
或 plpgsql
.
+1。重新设计您的 table 模式(稍微)
虽然上述这些解决方案 可以 工作,但它们会使您的数据变得不可靠 table。此外,如果您需要另一列,哪个值取决于这些行是否适用(如测试日期),这些新列是否需要 n/a状态也是?在我看来,不是。该数据应在不同的列中:
create table med_test (
-- ...
is_applicable boolean not null,
result boolean,
taken_at timestamp with time zone
);
如果你看上面一行table,你可以清楚地分开你的案例:
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);