Oracle:基于过滤器的默认列值

Posted

技术标签:

【中文标题】Oracle:基于过滤器的默认列值【英文标题】:Orace: Default column value based on a filter 【发布时间】:2015-02-26 07:56:26 【问题描述】:

您好,开发人员要求在表中添加一个默认值为“N”的列,但是如果条目的 id = 3,则该列的默认值应为“Y”,无论如何我可以在 oracle 中实现这一点吗?

【问题讨论】:

我通常从应用程序端处理这些事情。只需column = 'N'; if(id == 3) then column = 'Y'; -----store row in DB------。如果 DB 中存在现有行,则只需使用 case when 现有行语句更新列。 可能在插入期间为此添加触发器? VIRTUAL columnDECODE 函数就是你所需要的。 @Deepak Pawar:我通常不会在应用程序中处理这个问题,因为数据库完整性会受到威胁,因为您可能无法控制所有应用程序。此外,从维护的角度来看,许多应用程序可能需要更新。 记录的ID真的是3还是不是?然后我们在谈论一张唱片。插入 ID 为 3 且值为“Y”的记录,并将“N”设为列的默认值。 【参考方案1】:

我同意那些提到这不是一个好的数据库设计的评论者的观点。也就是说,在现实生活中对数据库设计做出妥协并不罕见。

我不确定是否需要虚拟列。 OP 要求提供一种默认方法;虚拟列的工作方式与默认约束不同(例如,使用默认约束,我们可以在列中插入默认值以外的值。采取的最佳途径可能是使用触发器来设置“默认”值:

CREATE OR REPLACE TRIGGER mytrigger
  BEFORE INSERT ON mytable FOR EACH ROW
  WHEN (new.mycolumn IS NULL)
BEGIN
  SELECT DECODE(id, 3, 'Y', 'N') INTO :new.mycolumn FROM dual;
END;
/

无论您使用的是 Oracle 10g 还是 11g(您都已标记),触发器也都可以工作。

希望这会有所帮助。

【讨论】:

如果您使用:new.mycolumn := CASE :new.id WHEN 3 THEN 'Y' ELSE 'N' END; 而不是SELECT,您可以节省从 PL/SQL 引擎跳转到 SQL 引擎并返回执行 IF 测试的成本。另外,我不相信您对id 的引用不会在前面没有:new. 的范围内。我也欢迎在这里使用:old 而不是:new 发表评论。 感谢您的评论,我认为您提到的一切都是正确的。也可以使用DECODE(:new.id, 3, 'Y', 'N') 而不是CASE 等。 请记住,DECODE 仅在 SQL 中有效,在 PL/SQL 中无效。 CASE 在这两种情况下都有效。【参考方案2】:

11g 方法

Oracle 11g 起,您可以使用VIRTUAL columns 一步完成。

测试用例

SQL> CREATE TABLE tab_default (
  2    ID          NUMBER,
  3    flag varchar2(1) GENERATED ALWAYS AS (decode(id, 3, 'Y', 'N')) VIRTUAL
  4  );

Table created.

SQL>
SQL> INSERT INTO tab_default (ID) VALUES (1);

1 row created.

SQL> INSERT INTO tab_default (ID) VALUES (3);

1 row created.

SQL> INSERT INTO tab_default (ID) VALUES (10);

1 row created.

SQL> SELECT * FROM tab_default;

        ID F
---------- -
         1 N
         3 Y
        10 N

SQL>

所以,VIRTUAL 列声明中的 DECODE 函数会为您处理需求。

10g 方法

您可以使用 -

来满足要求
    DEFAULTAFTER INSERT TRIGGER 每当 id = 3 时

创建表以将DEFAULT 值设为“N”。仅当插入新行且id column = 3 中的值时触发触发器,以便触发器将值更新为“Y”。否则对于所有其他情况,默认值为“N”。

【讨论】:

为什么要使用 AFTER 触发器而不是 BEFORE 触发器? 因为后触发器至少可以确保事务向前推进。如果您依赖 before trigger,并以此为基础进行更改,则可能无法保证。但是,不会有太大的不同,在这种情况下,我更喜欢后触发器。顺便说一句,我不是触发器的忠实粉丝,因为它们是效果的动作,因此是副作用。但是,在这种情况下,如果有人不在11g 上,那么这是唯一的方法。这就是我编辑答案并将11g 方法移到10gtrigger 方法之上的主要原因。【参考方案3】:

将新列添加到表后,您可以使用以下查询在列中插入值:

update table_name set column_name = ( case when id = 3 then 'Y'  else 'N' end );    

在插入新记录时,您可以使用以下方法: 1) 在创建插入查询时确定列,您可以在创建查询时为其添加逻辑。 2) 在数据库中创建一个触发器,在向表中插入任何新行后更新列值。

【讨论】:

【参考方案4】:

这是一个非常糟糕的数据库设计。它不尊重关系数据库的范式。 我建议保持该表不变,并在表上创建一个新视图,并使用 DECODE 或 CASE WHEN 计算的额外列...

【讨论】:

并非每个应用程序都可以在一夜之间修复。这就是为什么我们需要一些临时解决方法,直到找到并实施永久解决方案。【参考方案5】:

使用附加值列创建一个新表:

create table table1 as
select u.*,
case when id=3 then 'Y' ELSE 'N'
  END value  
from table2 u

【讨论】:

以上是关于Oracle:基于过滤器的默认列值的主要内容,如果未能解决你的问题,请参考以下文章

如果数据帧基于列值上的过滤器,则从字典中提取行数据

ag-grid:在列值上创建过滤器下拉列表?

具有列值的空格的材料表数据源过滤器

对列值使用变量提示和过滤语句

根据 C#2.0 中的列值过滤 DataTable 行

使用匹配的文本框和列值过滤表