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 column
和 DECODE
函数就是你所需要的。
@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 方法
您可以使用 -
来满足要求DEFAULT
值
AFTER INSERT TRIGGER
每当 id = 3 时
创建表以将DEFAULT
值设为“N”。仅当插入新行且id
column = 3 中的值时触发触发器,以便触发器将值更新为“Y”。否则对于所有其他情况,默认值为“N”。
【讨论】:
为什么要使用 AFTER 触发器而不是 BEFORE 触发器? 因为后触发器至少可以确保事务向前推进。如果您依赖 before trigger,并以此为基础进行更改,则可能无法保证。但是,不会有太大的不同,在这种情况下,我更喜欢后触发器。顺便说一句,我不是触发器的忠实粉丝,因为它们是效果的动作,因此是副作用。但是,在这种情况下,如果有人不在11g
上,那么这是唯一的方法。这就是我编辑答案并将11g
方法移到10g
trigger 方法之上的主要原因。【参考方案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:基于过滤器的默认列值的主要内容,如果未能解决你的问题,请参考以下文章