在Oracle中使列READONLY的最简单方法是什么?

Mik*_*sen 8 sql oracle oracle11g

我们有一个奇怪的神秘数据损坏错误,每隔几周弹出一次,没有人知道原因.到目前为止,似乎表上的主键是自发更改的,因此指向它的其他行现在搞砸了.

虽然我仍在寻找这个的根本原因(这是不可能重现的),但我想要某种临时的黑客来防止列值不断变化.这是表模式:

CREATE TABLE TPM_INITIATIVES  ( 
    INITIATIVEID    NUMBER NOT NULL,
    NAME            VARCHAR2(100) NOT NULL,
    ACTIVE          CHAR(1) NULL,
    SORTORDER       NUMBER NULL,
    SHORTNAME       VARCHAR2(100) NULL,
    PROJECTTYPEID   NUMBER NOT NULL,
    CONSTRAINT TPM_INITIATIVES_PK PRIMARY KEY(INITIATIVEID)
    NOT DEFERRABLE
     VALIDATE
)
Run Code Online (Sandbox Code Playgroud)

我们当然需要能够创建新行,但我想阻止ANYTHING INITIATIVEID永远改变,无论运行什么奇怪的查询.

我能想到的一些想法:

  • 我对Oracle的表权限并不是很熟悉(我更像是Postgres的人),但是你不能将某个列的权限更新或拒绝更新给所有用户吗?这只会影响更新,还是INSERTS?DENY更新到此列的命令是什么?
  • 创建一些在ROW UPDATE上运行的触发器.我们可以检测是否INITIATIVEID正在更改,如果是,则抛出异常或以某种方式炸毁?

至少,我们可以捕获和/或记录此事件以查看它何时发生以及导致INITIATIVEID更改的查询是什么?

谢谢!

Jus*_*ave 6

如果存在填充了引用该INITIATIVEID列的数据的子表,则Oracle应通过更改父级主键来阻止您创建孤立行,从而自动使更改主键值变得困难.因此,例如,如果存在具有外键约束的子表,TPM_INITIATIVES并且此子表中的行具有INITIATIVEID17,则您将无法更改当前值INITIATIVEID的TPM_INITIAITVES表中的行如果任何子表中没有引用表中特定行的行,则TPM_INITIATIVES可以更改该值,但是,如果没有关系,则更改主键值是不重要的,因为它不能,根据定义,导致数据完整性问题.当然,你可以有一个插入新行代码TPM_INITIATIVES用新的INITIATIVEID,改变所有子表中引用旧行指新行的行,然后修改旧行.但这不会受到任何提议的解决方案的困扰.

如果您的应用程序已定义子表但未声明适当的外键约束,那么这将是解决问题的最佳方法.

话虽这么说,Arnon的创建视图的解决方案应该有效.您将重命名该表,创建一个与现有表同名的视图,并(可能)在视图上定义一个永远不会更新INITIATIVEID列的INSTEAD OF触发器.这不应该要求更改应用程序的其他位.

您还可以在表上定义触发器

CREATE TRIGGER trigger_name 
  BEFORE UPDATE ON TPM_INITIATIVES  
  FOR EACH ROW
DECLARE
BEGIN
  IF( :new.initiativeID != :old.initiativeID )
  THEN
    RAISE_APPLICATION_ERROR( -20001, 'Sorry Charlie.  You can''t update the initiativeID column' );
  END IF;
END;
Run Code Online (Sandbox Code Playgroud)

当然,有人可以禁用触发器并发出更新.但我假设你并没有试图阻止攻击者,只是一个有缺陷的代码.

然而,根据您所看到的症状的描述,将更改历史记录记录到此表中的列中似乎更有意义,这样您就可以实际确定正在进行的操作而不是猜测并尝试插入一个逐之一.所以,例如,你可以做这样的事情

CREATE TABLE TPM_INITIATIVES_HIST (
   INITIATIVEID    NUMBER NOT NULL,
   NAME            VARCHAR2(100) NOT NULL,
   ACTIVE          CHAR(1) NULL,
   SORTORDER       NUMBER NULL,
   SHORTNAME       VARCHAR2(100) NULL,
   PROJECTTYPEID   NUMBER NOT NULL,
   OPERATIONTYPE   VARCHAR2(1) NOT NULL,
   CHANGEUSERNAME  VARCHAR2(30),
   CHANGEDATE      DATE,
   COMMENT         VARCHAR2(4000)
);

CREATE TRIGGER trigger_name 
  BEFORE INSERT or UPDATE or DELETE ON TPM_INITIATIVES  
  FOR EACH ROW
DECLARE
  l_comment VARCHAR2(4000);
BEGIN
  IF( inserting )
  THEN
    INSERT INTO tpm_initiatives_hist( INITIATIVEID, NAME, ACTIVE, SORTORDER, SHORTNAME, PROJECTTYPEID, 
                                      OPERATIONTYPE, CHANGEUSERNAME, CHANGEDATE )
      VALUES( :new.initiativeID, :new.name, :new.active, :new.sortOrder, :new.shortName, :new.projectTypeID, 
              'I', USER, SYSDATE );
  ELSIF( inserting )
  THEN
    IF( :new.initiativeID != :old.initiativeID )
    THEN
      l_comment := 'Initiative ID changed from ' || :old.initiativeID || ' to ' || :new.initiativeID;
    END IF;
    INSERT INTO tpm_initiatives_hist( INITIATIVEID, NAME, ACTIVE, SORTORDER, SHORTNAME, PROJECTTYPEID, 
                                      OPERATIONTYPE, CHANGEUSERNAME, CHANGEDATE, COMMENT )
      VALUES( :new.initiativeID, :new.name, :new.active, :new.sortOrder, :new.shortName, :new.projectTypeID, 
              'U', USER, SYSDATE, l_comment );
  ELSIF( deleting )
  THEN
    INSERT INTO tpm_initiatives_hist( INITIATIVEID, NAME, ACTIVE, SORTORDER, SHORTNAME, PROJECTTYPEID, 
                                      OPERATIONTYPE, CHANGEUSERNAME, CHANGEDATE )
      VALUES( :old.initiativeID, :old.name, :old.active, :old.sortOrder, :old.shortName, :old.projectTypeID, 
              'D', USER, SYSDATE );
  END IF;
END;
Run Code Online (Sandbox Code Playgroud)

然后,您可以查询TPM_INITIATIVES_HIST以查看随时间对特定行所做的所有更改.因此,您可以查看主键值是否正在更改,或者某人是否只是更改非键字段.理想情况下,您可以添加其他列以添加到历史记录表中以帮助跟踪更改(即可能存在V$SESSION可能有用的内容).


Arn*_*-Oz 5

隐藏视图后面的表并使更新触发器更新除要保护的列之外的所有内容

  • 为什么?- 你改变表 <name> 重命名为 <temp_Name> 然后在旧名称上创建视图所以一切对应用程序的其余部分都是透明的 (2认同)