What algorithms are known to perform the task of updating a database by inserting, updating, and deleting rows in the presence of database constraints?
More specifically
OK, I think that this is it, though the Unique Key thing is pretty hard to figure out. Note that any errors encountered in the SQL execution should result in complete rollback of the entire transaction.
UPDATE: The original order that I implemented was:
Each Table, BottumUp(All Deletes for table) Each Table, TopDown(All Updates, then All Inserts)
After a counter-example was posted, I believe that I know haw to correct for the restriced problem only (problem #1, without UCs): by changing the order to:
Each Table, TopDown(All Inserts) Each Table, TopDown(All Updates) Each Table, BottumUp(All Deletes)
This will definitely NOT work with Unique Constraints though, which as far as I can figure will need a row-content based dependency sort (as opposed to the static table FK dependency sort I am currently using). What makes this particularily difficult is that it may require getting info about record-content other than the changed ones (in particular checking for the existence of UC conflict-values and child-dependent records for intermediate steps).
Anyway, here's the current version:
Public Class TranformChangesToSQL
Class ColVal
Public name As String
Public value As String 'note: assuming string values'
End Class
Class Row
Public Columns As List(Of ColVal)
End Class
Class FKDef
'NOTE: all FK''s are assumed to be of the same type: records in the FK table'
' must have a record in the PK table matching on FK=PK columns.'
Public PKTableName As String
Public FKTableName As String
Public FK As String
End Class
Class TableInfo
Public Name As String
Public PK As String 'name of the PK column'
Public UniqueKeys As List(Of String) 'column name of each Unique key'
'This table''s Foreign Keys (FK):'
Public DependsOn As List(Of FKDef)
'Other tables FKs that point to this table'
Public DependedBy As List(Of FKDef)
Public Columns As List(Of String)
'note: all row collections are indexed by PK'
Public inserted As List(Of Row) 'inserted after-images'
Public deleted As List(Of Row) 'deleted before-images'
Public updBefore As List(Of row)
Public updAfter As List(Of row)
End Class
Sub MakeSQL(ByVal tables As List(Of TableInfo))
'Note table dependencies(FKs) must NOT form a cycle'
'Sort the tables by dependency so that'
' child tables (FKs) are always after their parents (PK tables)'
TopologicalSort(tables)
For Each tbl As TableInfo In tables
'Do INSERTs, they *must* be done first in parent-> child order, because:'
' they may have FKs dependent on parent inserts'
' and there may be Updates that will make child records dependent on them'
For Each r As Row In tbl.inserted
Dim InsSQL As String = "INSERT INTO " & tbl.Name & "("
Dim valstr As String = ") VALUES("
Dim comma As String = ""
For Each col As ColVal In r.Columns
InsSQL = InsSQL & comma & col.name
valstr = valstr & comma & "'" & col.value & "'"
comma = ", " 'needed for second and later columns'
Next
AddSQL(InsSQL & valstr & ");")
Next
Next
For Each tbl As TableInfo In tables
'Do UPDATEs'
For Each aft In tbl.updAfter
'get the matching before-update row'
Dim bef As Row = tbl.updBefore(aft.Columns(tbl.PK.ColName).value)
Dim UpdSql As String = "UPDATE " & tbl.Name & " SET "
Dim comma As String = ""
For Each col As ColVal In aft.Columns
If bef.Columns(col.name).value <> col.value Then
UpdSql = UpdSql & comma & col.name & " = '" & col.value & "'"
comma = ", " 'needed for second and later columns'
End If
Next
'only add it if any columns were different:'
If comma <> "" Then AddSQL(UpdSql & ";")
Next
Next
'Now reverse it so that INSERTs & UPDATEs are done in parent->child order'
tables.Reverse()
For Each tbl As TableInfo In tables.Reverse
'Do DELETEs, they *must* be done last, and in child->paernt order because:'
' Parents may have children that depend on them, so children must be deleted first,'
' and there may be children dependent until after Updates pointed them away'
For Each r As Row In tbl.deleted
AddSQL("DELETE From " & tbl.Name & " WHERE " & tbl.PK.ColName & " = '" & r.Columns(tbl.PK.ColName).value) & "';"
Next
Next
End Sub
End Class