数据库设计:用时间戳列替换布尔列?

Igo*_*hin 8 sql database oracle database-design relational-database

之前我用这种方式创建了表:

create table workflow (
    id number primary key,
    name varchar2(100 char) not null,
    is_finished number(1) default 0 not null,
    date_finished date
);
Run Code Online (Sandbox Code Playgroud)

列is_finished指示工作流程是否已完成.列date_finished是工作流完成的时间.

然后我的想法是"我不需要is_finished,因为我可以说:data_finished不是null",我设计的没有is_finished列:

create table workflow (
    id number primary key,
    name varchar2(100 char) not null,
    date_finished date
);
Run Code Online (Sandbox Code Playgroud)

(我们使用Oracle 10)

这是一个好主意还是坏主意?我听说你不能在具有NULL值的列上有索引,所以where data_finished is not null在大表上会非常慢.

OMG*_*ies 15

这是一个好主意还是坏主意?

好主意.

你已经消除了冗余列占用的空间; DATE列提供双重任务 - 您知道工作已完成,何时完成.

我听说你不能在具有NULL值的列上有索引,所以"data_finished不为null"在大表上会非常慢.

那是不对的.Oracle索引忽略NULL值.

您可以创建一个基于函数的索引,以便绕过未被索引的NULL值,但我遇到的大多数DBA 确实不喜欢它们,所以要为战斗做好准备.

  • 好主意.如果您只有布尔字段,则会丢失关键信息.如果你把date_finished列,它很容易理解和查询,并更好地表示现实世界:如果它是null,它只是意味着它还没有完成.有两个字段没有规范化(有is_finished和date_finished):它是冗余的,冗余会导致不一致(即,你可能连续存在不一致的值)和对数据缺乏信任,以及容易出错的查询和程序. (2认同)

Ada*_*kes 11

有一种正确的索引空值的方法,它不使用FBI.Oracle 索引空值,但它不会索引树中的空LEAF值.因此,您可以消除列is_finished并创建这样的索引.

CREATE INDEX ON workflow (date_finished, 1);
Run Code Online (Sandbox Code Playgroud)

然后,如果您检查此查询的解释计划:

SELECT count(*) FROM workflow WHERE date_finished is null;
Run Code Online (Sandbox Code Playgroud)

您可能会看到正在使用的索引(如果优化器很满意).

回到原来的问题:在这里查看各种答案,我认为没有正确的答案.如果没有必要,我可能有个人偏好消除列,但我也不喜欢重载列的含义.这里有两个概念:

  1. 记录已经完成. is_finished
  2. 该记录在特定日期结束. date_finished

也许你需要保持这些独立,也许你不需要.当我考虑消除is_finished色谱柱时,它让我感到困扰.在路上,记录完成的情况可能会出现,但你不确切知道何时.也许您必须从​​其他来源导入数据并且日期未知.当然,这不符合现在的业务需求,但事情会发生变化.那你怎么办呢?好吧,你必须在date_finished列中放入一些虚拟值,现在你已经有点破坏了数据.不是很可怕,但那里有一个磨擦.当我做那样的事情的时候,我脑子里的小声音在喊着做错了.

我的建议,保持分开.你在谈论一个很小的列和一个非常瘦的索引.存储不应成为问题.

表示规则:将知识折叠成数据,因此程序逻辑可以是愚蠢和健壮的.

-Eric S. Raymond


Ste*_*age 5

对于所有说该专栏浪费空间的人:

双重职责在数据库中并不是一件好事。您的主要目标应该是清晰。许多系统、工具、人们都会使用你的数据。如果您通过将含义隐藏在其他列中来掩饰值,那么您就是在乞求其他系统或用户弄错。

任何认为它可以节省空间的人都是完全错误的。

您将需要该日期列上的两个索引...一个将是基于函数的,正如 OMG 建议的那样。它看起来像这样:

NVL(Date_finished, TO_DATE('01-JAN-9999'))

因此,要找到未完成的作业,您必须确保正确编写 where 子句

它看起来像这样:

WHERE NVL(Date_finished, TO_DATE('01-JAN-9999')) = TO_DATE('01-JAN-9999')

是的。那是那么清楚。它完全比

WHERE IS_Unfinished = '是'

您希望在同一列上有第二个索引的原因是对于该日期的所有其他查询...您不会希望使用该索引按日期查找工作。

那么让我们看看您根据 OMG 的建议等取得了哪些成果。

您使用了更多的空间,您混淆了数据的含义,您更有可能犯错误......胜利者!

有时,程序员似乎还生活在 70 年代,当时 1MB 的硬盘空间就是一套房子的首付。

您可以在不放弃大量清晰度的情况下节省空间。将 Is_unfinished 设置为 Y 或 NULL...如果您仅使用该列来查找“要做的工作”。这将使该索引保持紧凑。它只会与未完成的行一样大(通过这种方式,您可以利用未索引的空值,而不是被它搞砸)。你在桌子上放了一点空间,但总的来说,它比 FBI 的空间要小。该列需要 1 个字节,并且只对未完成的行进行索引,这样,这只是工作的一小部分,并且可能保持相当恒定。无论您是否试图找到它们,FBI 的每一行都需要 7 个字节。该索引将与表的大小保持同步,而不仅仅是与未完成作业的大小保持同步。

回复 OMG 的评论

在他/她的评论中,他/她指出要找到未完成的工作,您只需使用

WHERE date_finished IS NULL
Run Code Online (Sandbox Code Playgroud)

但他在回答中说

您可以创建基于函数的索引,以避免未索引的 NULL 值

如果您按照他指向的链接操作,使用 NVL 将空值替换为其他任意值,那么我不确定还有什么需要解释。

  • 我在某种程度上同意你的观点,斯蒂芬妮,但在这种情况下,这是非常明显的。我认为(当然,这是一种观点)大多数开发人员都会凭直觉认为“date_finished IS NULL”意味着“没有完成的日期,所以这意味着它尚未完成”。恕我直言,是否需要额外的 Y/N 列需要根据具体情况进行考虑。 (4认同)
  • @Stephanie - 为什么不能创建一个名为 vCompletedWorkFlow 的视图来过滤掉所有空值并告诉业务人员在其报告中使用该视图? (4认同)
  • 要查找未完成的作业,您只需使用“WHERE date_finished IS NULL”。你没有解释哪里浪费了空间——你只是漫无目的地谈论需要两个索引。并且没有任何证据表明可为 NULL 的日期列会混淆数据。 (3认同)

Qua*_*noi 5

这是一个好主意还是坏主意?我听说你不能在具有NULL值的列上有索引,所以"data_finished不为null"在大表上会非常慢.

Oracle确实为可空字段编制索引,但不对索引值进行索引NULL

这意味着您可以在标记的字段上创建索引NULL,但保留NULL在此字段中的记录不会使其成为索引.

反过来,这意味着如果你做date_finished NULL,索引的大小更小,因为这些NULL值不会存储在索引中.

因此,涉及范围搜索相等的查询date_finished实际上会表现得更好.

当然,这个解决方案的缺点是涉及NULL值的查询date_finished必须恢复到全表扫描.

您可以通过创建两个索引来解决此问题:

CREATE INDEX ON mytable (date_finished)
CREATE INDEX ON mytable (DECODE(date_finished, NULL, 1))
Run Code Online (Sandbox Code Playgroud)

并使用此查询来查找未完成的工作:

SELECT  *
FROM    mytable
WHERE   DECODE(date_finished, NULL, 1) = 1
Run Code Online (Sandbox Code Playgroud)

这将像分区索引一样:完整的作品将被第一个索引索引; 不完整的将由第二个索引.

如果您不需要搜索完整或不完整的作品,则可以随时删除相应的索引.