Bagger said:
The problem I'm having
is that when I try to update a contract_number in tb_Contract, the cascading
update fails with the error message:
"-2147467259 : Cannot perform cascading operation. There must be a related
record in table 'tb_mod'."
Is there some other way I should be setting this up? Is there a way to fix
the problem so that I don't have to break the relationship?
So I didn't convince you that posting SQL DDL is the way to go, eh <g>?
The problem is that your hierarchy diverges after the tb_DO level then
converges again at the tb_Mod_Detail level, where the Base_ID,
Contract_Number and DO_Number columns (compound) reference two tables.
There is a solution to this problem (without a paradigm shift away from
natural keys - nice try, tina <g>) but it doesn't look too pretty.
Basically, duplicate the columns as necessary so that each is
referencing only one table. This will give you a nine column PK,
something like
Base_ID_in_tb_MOD,
Contract_Number_in_tb_MOD,
DO_Number_in_tb_MOD,
Mod_Number,
Base_ID_in_tb_Funding_Area,
Contract_Number_in_tb_Funding_Area,
DO_Number_in_tb_Funding_Area,
Funding_Area,
Project_Number
Here's the SQL DDL to recreate the problem and demo the proposed
solution:
Sub testcas()
Kill "C:\DropMe1.mdb"
Dim cat
Set cat = CreateObject("ADOX.Catalog")
With cat
.Create _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\DropMe1.mdb"
With .ActiveConnection
.Execute _
"CREATE TABLE tb_Contract ( Base_ID INT NOT" & _
" NULL, Contract_Number INT NOT NULL, PRIMARY" & _
" KEY (Base_ID, Contract_Number));"
.Execute _
"CREATE TABLE tb_DO ( Base_ID INT NOT NULL," & _
" Contract_Number INT NOT NULL, DO_Number" & _
" INT NOT NULL, PRIMARY KEY (Base_ID, Contract_Number," & _
" DO_Number), FOREIGN KEY (Base_ID, Contract_Number)" & _
" REFERENCES tb_Contract (Base_ID, Contract_Number)" & _
" ON DELETE CASCADE ON UPDATE CASCADE );"
.Execute _
"CREATE TABLE tb_Mod ( Base_ID INT NOT NULL," & _
" Contract_Number INT NOT NULL, DO_Number" & _
" INT NOT NULL, Mod_Number INT NOT NULL," & _
" PRIMARY KEY (Base_ID, Contract_Number," & _
" DO_Number, Mod_Number), FOREIGN KEY (Base_ID," & _
" Contract_Number, DO_Number) REFERENCES" & _
" tb_DO (Base_ID, Contract_Number, DO_Number)" & _
" ON DELETE CASCADE ON UPDATE CASCADE );"
.Execute _
"CREATE TABLE tb_Funding_Area ( Base_ID INT" & _
" NOT NULL, Contract_Number INT NOT NULL," & _
" DO_Number INT NOT NULL, Funding_Area INT" & _
" NOT NULL, PRIMARY KEY (Base_ID, Contract_Number," & _
" DO_Number, Funding_Area), FOREIGN KEY (Base_ID," & _
" Contract_Number, DO_Number) REFERENCES" & _
" tb_DO (Base_ID, Contract_Number, DO_Number)" & _
" ON DELETE CASCADE ON UPDATE CASCADE );"
.Execute _
"CREATE TABLE tb_Mod_Detail ( Base_ID INT" & _
" NOT NULL, Contract_Number INT NOT NULL," & _
" DO_Number INT NOT NULL, Mod_Number INT" & _
" NOT NULL, Funding_Area INT NOT NULL, Project_Number" & _
" INT NOT NULL, PRIMARY KEY (Base_ID, Contract_Number," & _
" DO_Number, Mod_Number, Funding_Area, Project_Number)," & _
" FOREIGN KEY (Base_ID, Contract_Number," & _
" DO_Number, Funding_Area) REFERENCES tb_Funding_Area" & _
" (Base_ID, Contract_Number, DO_Number, Funding_Area)" & _
" ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN" & _
" KEY (Base_ID, Contract_Number, DO_Number," & _
" Mod_Number) REFERENCES tb_MOD (Base_ID," & _
" Contract_Number, DO_Number, Mod_Number)" & _
" ON DELETE CASCADE ON UPDATE CASCADE);"
.Execute _
"INSERT INTO tb_Contract (Base_ID, Contract_Number)" & _
" VALUES (1, 1);"
.Execute _
"INSERT INTO tb_DO (Base_ID, Contract_Number," & _
" DO_Number) VALUES (1, 1, 2);"
.Execute _
"INSERT INTO tb_Mod (Base_ID, Contract_Number," & _
" DO_Number, Mod_Number) VALUES (1, 1, 2," & _
" 3);"
.Execute _
"INSERT INTO tb_Funding_Area (Base_ID, Contract_Number," & _
" DO_Number, Funding_Area) VALUES (1, 1," & _
" 2, 4);"
.Execute _
"INSERT INTO tb_Mod_Detail (Base_ID, Contract_Number," & _
" DO_Number, Mod_Number, Funding_Area, Project_Number)" & _
" VALUES (1, 1, 2, 3, 4, 5);"
Dim rs
Set rs = .Execute( _
"SELECT Base_ID, Contract_Number, DO_Number," & _
" Mod_Number, Funding_Area, Project_Number" & _
" FROM tb_Mod_Detail;")
MsgBox rs.GetString
rs.Close
On Error Resume Next
' UPDATE and CASCADE will fail
.Execute _
"UPDATE tb_Contract SET Contract_Number =" & _
" 99, Base_ID = 55 WHERE Contract_Number" & _
" = 1 AND Base_ID = 1;"
If Err.Number <> 0 Then
MsgBox Err.Description
Else
Set rs = .Execute( _
"SELECT Base_ID, Contract_Number, DO_Number," & _
" Mod_Number, Funding_Area, Project_Number" & _
" FROM tb_Mod_Detail;")
MsgBox rs.GetString
rs.Close
End If
On Error GoTo 0
' Fix the schema
.Execute _
"DROP TABLE tb_Mod_Detail "
.Execute _
"CREATE TABLE tb_Mod_Detail ( Base_ID_in_tb_MOD" & _
" INT NOT NULL, Contract_Number_in_tb_MOD" & _
" INT NOT NULL, DO_Number_in_tb_MOD INT NOT" & _
" NULL, Mod_Number INT NOT NULL, Base_ID_in_tb_Funding_Area" & _
" INT NOT NULL, Contract_Number_in_tb_Funding_Area" & _
" INT NOT NULL, DO_Number_in_tb_Funding_Area" & _
" INT NOT NULL, Funding_Area INT NOT NULL," & _
" Project_Number INT NOT NULL, PRIMARY KEY" & _
" (Base_ID_in_tb_MOD, Contract_Number_in_tb_MOD," & _
" DO_Number_in_tb_MOD, Mod_Number, Base_ID_in_tb_Funding_Area," & _
" Contract_Number_in_tb_Funding_Area,
DO_Number_in_tb_Funding_Area," & _
" Funding_Area, Project_Number), FOREIGN" & _
" KEY (Base_ID_in_tb_Funding_Area,
Contract_Number_in_tb_Funding_Area," & _
" DO_Number_in_tb_Funding_Area, Funding_Area)" & _
" REFERENCES tb_Funding_Area (Base_ID, Contract_Number," & _
" DO_Number, Funding_Area) ON DELETE CASCADE" & _
" ON UPDATE CASCADE, FOREIGN KEY (Base_ID_in_tb_MOD," & _
" Contract_Number_in_tb_MOD, DO_Number_in_tb_MOD," & _
" Mod_Number) REFERENCES tb_MOD (Base_ID," & _
" Contract_Number, DO_Number, Mod_Number)" & _
" ON DELETE CASCADE ON UPDATE CASCADE);"
.Execute _
"INSERT INTO tb_Mod_Detail (Base_ID_in_tb_MOD," & _
" Contract_Number_in_tb_MOD, DO_Number_in_tb_MOD," & _
" Mod_Number, Base_ID_in_tb_Funding_Area," & _
" Contract_Number_in_tb_Funding_Area,
DO_Number_in_tb_Funding_Area," & _
" Funding_Area, Project_Number) VALUES (1," & _
" 1, 2, 3, 1, 1, 2, 4, 5);"
Set rs = .Execute( _
"SELECT Base_ID_in_tb_MOD, Contract_Number_in_tb_MOD," & _
" DO_Number_in_tb_MOD, Mod_Number, Base_ID_in_tb_Funding_Area," & _
" Contract_Number_in_tb_Funding_Area,
DO_Number_in_tb_Funding_Area," & _
" Funding_Area, Project_Number FROM tb_Mod_Detail;")
MsgBox rs.GetString
rs.Close
On Error Resume Next
' UPDATE and CASCADE will succeed
.Execute _
"UPDATE tb_Contract SET Contract_Number =" & _
" 99, Base_ID = 55 WHERE Contract_Number" & _
" = 1 AND Base_ID = 1;"
If Err.Number <> 0 Then
MsgBox Err.Description
Else
Set rs = .Execute( _
"SELECT Base_ID_in_tb_MOD, Contract_Number_in_tb_MOD," & _
" DO_Number_in_tb_MOD, Mod_Number, Base_ID_in_tb_Funding_Area," & _
" Contract_Number_in_tb_Funding_Area,
DO_Number_in_tb_Funding_Area," & _
" Funding_Area, Project_Number FROM tb_Mod_Detail;")
MsgBox rs.GetString
rs.Close
End If
On Error GoTo 0
End With
Set .ActiveConnection = Nothing
End With
End Sub
Jamie.
--