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永远改变,无论运行什么奇怪的查询.
我能想到的一些想法:
INITIATIVEID正在更改,如果是,则抛出异常或以某种方式炸毁?至少,我们可以捕获和/或记录此事件以查看它何时发生以及导致INITIATIVEID更改的查询是什么?
谢谢!
如果存在填充了引用该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可能有用的内容).
隐藏视图后面的表并使更新触发器更新除要保护的列之外的所有内容