Hvis der er underordnede tabeller udfyldt med data, der refererer til INITIATIVEID
kolonne, skulle Oracle automatisk gøre det vanskeligt at ændre den primære nøgleværdi ved at forhindre dig i at oprette forældreløse rækker ved at ændre forælderens primære nøgle. Så hvis der for eksempel er en undertabel, der har en fremmednøgle-begrænsning til TPM_INITIATIVES
og der er en række i denne underordnede tabel med et INITIATIVEID
af 17, vil du ikke være i stand til at ændre INITIATIVEID
af rækken i TPM_INITIAITVES
tabel, hvis aktuelle værdi er 17. Hvis der ikke er nogen række i nogen underordnet tabel, der refererer til den bestemte række i TPM_INITIATIVES
tabel, kan du ændre værdien, men hvis der ikke er nogen relationer, er ændring af den primære nøgleværdi uvigtig, da det ikke pr. definition kan forårsage et dataintegritetsproblem. Selvfølgelig kan du have kode, der indsætter en ny række i TPM_INITIATIVES
med et nyt INITIATIVEID
, ændre alle rækkerne i den underordnede tabel, der refererer til den gamle række, til at henvise til den nye række, og modificer derefter den gamle række. Men dette vil ikke blive fanget af nogen af de foreslåede løsninger.
Hvis din applikation har defineret underordnede tabeller, men ikke erklæret de passende fremmednøglebegrænsninger, ville det være den bedste måde at løse problemet på.
Når det er sagt, så burde Arnons løsning med at skabe en udsigt virke. Du ville omdøbe tabellen, oprette en visning med samme navn som den eksisterende tabel og (potentielt) definere en ISTEDEN FOR trigger på visningen, der simpelthen aldrig ville opdatere INITIATIVEID
kolonne. Det burde ikke kræve ændringer af andre dele af applikationen.
Du kan også definere en trigger på bordet
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;
Nogen kunne selvfølgelig deaktivere triggeren og udstede en opdatering. Men jeg går ud fra, at du ikke forsøger at stoppe en angriber, bare et buggy stykke kode.
Baseret på beskrivelsen af, hvilke symptomer du ser, ser det dog ud til at være mere fornuftigt at logge historikken for ændringer af kolonner i denne tabel, så du faktisk kan bestemme, hvad der foregår i stedet for at gætte og prøve at lukke huller en. - efter en. Så du kunne for eksempel gøre sådan noget
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;
Derefter kan du forespørge TPM_INITIATIVES_HIST
for at se alle de ændringer, der var blevet foretaget i en bestemt række over tid. Så du kan se, om de primære nøgleværdier ændrer sig, eller om nogen bare ændrer de ikke-nøglefelter. Ideelt set kan du have yderligere kolonner, som du kan tilføje til historiktabellen for at hjælpe med at spore ændringerne (dvs. måske er der noget fra V$SESSION
det kan være nyttigt).