如何创建一个4VL" boolean"输入PostgreSQL?

时间:2015-01-30 08:46:08

标签: postgresql logic relational-database relational-algebra

我正在寻找一个根据普通布尔类型同时具有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'的真值表的能力。值。

1 个答案:

答案 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,但我不确定会发生什么以及您的更改可能不会是可导出/可转储的等等。这意味着,您只能使用定义的值(falsena&amp; true)进行输入,不能使用别名(例如f,可以使用1boolean)(从text转换为完全不同的故事,甚至可能与某种类型的IO函数不一致。)

SQLFiddle

<强> 2。 Create真正的user-defined type

使用此选项,您可以创建一个真正的4VL布尔类型,但它带有成本,它将比enum选项更加有效。这些类型通常用C编写(或者用一种语言编写,它与PostgreSQL的源代码有适当的绑定)。您无法在sqlplpgsql中编写这些低级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);