Hi Dhruv,
Let us do some calculational math then. First, terminology.
Let Table be our table, TLkey be the key field that identifies our
timeline, FTS be the From Timestamp field TTS be the To Timestamp
field, and Data our data field. I am deliberately oversimpifying here.
We also have UTable, our update table. It also has TLkey, FTS and
Data.
Let T, T0, T1, ... be the relation that represents the value of Table
at various moments. We say T = Table.relation.
Let U, U0, U1, ... be the relation that represents the value of UTable
at various moments. We say U = UTable.relation.
Let t, t0, t1, ... be the elements of any of T, T0, T1, ...
Let u, u0, u1, ... be the elements of any of U, U0, U1, ...
These represent the rows of Table and UTable respectively.
We postulate that the values of TLkey, FTS and TTS are totally
ordered. TTS can also take the value eternity so that [t.TTS ≤
eternity]. Otherwise FTS and TTS can take the same values.
We define for both t, u, here x, x.key as totally ordered where for
any x0, x1,
x0.key ≤ x1.key ≡ x0.TLkey < x1.TLKey ∨ (x0.TLkey = x1.TLKey ∧ x0.FTS
≤ x1.FTS).
I won't prove here that key is totally ordered equivales TLkey and FTS
are totally ordered.
Our "MERGE" program has precondition
P: T0 = Table.relation ∧ U0 = UTable.relation,
and postcondition
R: T1 = Table.relation
∧ 〈A t, u : t∈T0 ∧ u∈U0 ∧ t.key=u.key : 〈E t0 : t0∈T1 :
t0.key=t.key ∧ t0.Data=u.Data ∧ t0.TTS=t.TTS 〉〉
∧ 〈A t : t∈T0 : ~〈E u: u∈U0 : t.key=u.key〉 : 〈E t0 : t0∈T1 :
t0.key=t.key ∧ t0.Data=t.Data ∧ t0.TTS=t.TTS 〉〉
∧ 〈A u : u∈U0 : ~〈E t: t∈T0 : t.key=u.key〉 : 〈E t : t∈T1 :
t.key=u.key ∧ t.Data=u.Data ∧ t.TTS=eternity 〉〉
∧ 〈A t : t∈T1 : 〈E t0: t0∈T0 : t.key=t0.key〉 ∨ 〈E u: u∈U0 :
t.key=u.key〉〉
I will not include the SQL here, but it is straightforward. In
essence, it uses an INSERT statement to establish the second clause
and an UPDATE statement to establish the fourth clause. The third
clause is established by the precondition. The final clause says that
we don't do anything else. The INSERT uses a subquery or LEFT OUTER
JOIN to confirm that the row does not yet exist, and the UPDATE uses
an INNER JOIN to join to existing rows.
Our "FIX TTS" has the precondition
P: T0 = Table.relation
and establishes the postcondition
R: T1 = Table.relation
∧ 〈A t, t0 : t∈T1 ∧ t0∈T0 ∧ t.key=t0.key : t.Data = t0.Data〉
∧ 〈A t : t∈T1 : 〈E t0 : t0∈T0 : t.key=t0.key〉〉
∧ 〈A t : t∈T0 : 〈E t0 : t0∈T1 : t.key=t0.key〉〉
∧ 〈A t : t∈T1 : 〈E t0 : t0∈T1 : t.TLkey=t0.TLkey ∧ t.FTS>t0.FTS
∧ ~〈E t1 : t1∈T1 : t.TLkey=t1.TLkey ∧ t.FTS>t1.FTS ∧
t1.FTS>t1.FTS〉
∧ t.TTS=t0.FTS-δ〉〉
∧ 〈A t : t∈T1 : ~〈E t0 : t0∈T1 : t.TLkey=t0.TLkey ∧ t.FTS>t0.FTS〉
∧ t.TTS=eternity〉
where δ is the smallest possible time interval. Here the second, third
and fourth clauses say that we only change TTS, and the final two
clauses spell out the changes if a successor exists, and if there is
no successor.
The obvious implementation uses an UPDATE statement with two LEFT
OUTER JOINS from the Table to itself, all of which equal TLKey, both
have FTS greater than the first table, and the third has FTS less than
the second. The WHERE clause ensures that the third row does not exist
(i.e. the second, if any, is the immediate successor of the first).
TTS is set to eternity if the second row does not exist, otherwise it
sets it to the FTS on the second row less a small amount.
Like I said before, this is not the issue. The issue is that we can
assume that before the MERGE, we have the precondition:
P: T0 = Table.relation ∧ U0 = UTable.relation,
∧ 〈A t : t∈T0 : 〈E t0 : t0∈T0 : t.TLkey=t0.TLkey ∧ t.FTS>t0.FTS
∧ ~〈E t1 : t1∈T0 : t.TLkey=t1.TLkey ∧ t.FTS>t1.FTS ∧
t1.FTS>t1.FTS〉
∧ t.TTS=t0.FTS-δ〉〉
∧ 〈A t : t∈T0 : ~〈E t0 : t0∈T0 : t.TLkey=t0.TLkey ∧ t.FTS>t0.FTS〉
∧ t.TTS=eternity〉
and its postcondition asserts that TTS is unchanged for all rows in
the table before the update. Because this table records the history of
the data values of some entities, I posit that for each TLkey, there
is a minimum value of MFTS before which TTS already has the correct
value. The precondition for "FIX TTS" then becomes:
P: T0 = Table.relation
∧ 〈A t : t∈T0 ∧ t.FTS < t.MFTS : 〈E t0 : t0∈T0 : t.TLkey=t0.TLkey
∧ t.FTS>t0.FTS
∧ ~〈E t1 : t1∈T0 : t.TLkey=t1.TLkey ∧ t.FTS>t1.FTS ∧
t1.FTS>t1.FTS〉
∧ t.TTS=t0.FTS-δ〉〉
∧ 〈A t : t∈T0 ∧ t.FTS < t.MFTS : ~〈E t0 : t0∈T0 :
t.TLkey=t0.TLkey ∧ t.FTS>t0.FTS〉
∧ t.TTS=eternity〉
The point of my original posting was to establish the value of MFTS.
I have the following observations.
0. MFTS depends on UTable.relation, in particular on the least FTS for
each TLkey that did not match an element of Table.relation.
1. MERGE set TTS to eternity for each inserted row.
2. If the first inserted row t has a predecessor t0, then t0.TTS is
invalid, but all its predecessors are valid, then t0.MFTS = t0.FTS.
3. If the first inserted row t does not have a predecessor, then
t.MFTS = t.FTS.
4. If we don't attempt this optimization, we should still compare
t.TTS with t0.FTS-δ, and not update when they are already equal. At
least we avoid the cost of all those saves to disk.
> Maybe you can try the workaround mentioned in this articles below.
http://www.aspfaq.com/sql2005/show.asp?id=11http://channel9.msdn.com/forums/Coffeehouse/102407-SQL-Server-and-LIM...
> ...
>
> read more »