postgresql - 如何在 PostgreSQL 中创建 4VL "boolean"类型?

标签 postgresql logic relational-database relational-algebra

我正在寻找一种类型,根据正常的 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,但我不确定会发生什么&您的更改可能无法导出/转储等)。这意味着,您只能使用定义的值(falsenatrue)进行输入,不能使用别名 (如 f1boolean)可以使用(从 text 进行转换是一个完全不同的故事,甚至可能与类型的 IO 函数不一致)。

SQLFiddle

<强>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/

相关文章:

javascript - 如何在 Sequelize JS 和 PostgreSQL 中的 1 :M relationship when implementing with . bulkCreate() 中使用外键

python - 从数据框中排除行,除非满足另一个列条件

Javascript 比较逻辑 - 让 a = 1 。 a === (3 || 1 ) 错误,为什么?

mysql - 涉及多个实体时的数据库建模

mysql - 数据库:如何将 ID 与每个表相关联? (多对多对多!)

node.js - Node Postgres SequelizeConnectionError : password authentication failed for user

postgresql - pg_dump postgresql 9.5 上目录丢失错误

postgresql - 在 PostgreSQL 中复制表 FROM 错误

sql - 使用SQL条件检查时间间隔

database-design - 时空数据的表设计