6.5 Generating Current Snapshots
6.5.1 Problem
You have an audit log from which you
need
to generate
a
current snapshot of the table on which the log is based. In other
words, you want to generate the original table from the log.
6.5.2 Solution
The following query can be executed against the StockLog table to
regenerate the data in the Stock table:
SELECT ProductID, Qty
FROM StockLog
WHERE LogId IN
(SELECT
(SELECT (CASE WHEN MAX(Type)!='D'
THEN MAX(s.LogID)
ELSE 0 END)
FROM StockLog s1
WHERE s.ProductID=s1.ProductID
AND s1.LogID=MAX(s.LogID))
FROM StockLog s
GROUP BY ProductID)
The generated result is as follows:
ProductID Qty
---------- ----
Apple 20
Orange 30
...
6.5.3 Discussion
Another use for a row-level audit-log table, such as the
StockLogtable, is to regenerate the data for
the table being logged. For example, you might want to do this if you
had accidentally deleted the main table.
The query shown in this recipe lists the ProductID and Qty columns
for the most recent version of each row in the Stock table.
Information for deleted records, where the log type code is a
D, is not returned by the query. To explain the
logic behind this query, it's best to look at the
subquery separately. To begin with, the following pseudocode captures
the essence of the function of the main query:
SELECT ProductID, Qty
FROM StockLog
WHERE LogID IN [most recent valid log entry
for each product, excluding deleted rows]
In the query that returns the most recent log entries for each
product, a subquery is used to return the LogID of the most recent
log entry for each product that doesn't represent a
delete operation. The code to exclude the deleted rows is highlighted
in the following example:
SELECT ProductID, Qty
FROM StockLog
WHERE LogId IN
(SELECT
(SELECT (CASE WHEN MAX(Type)!='D'
THEN MAX(s.LogID)
ELSE 0 END)
FROM StockLog s1
WHERE s.ProductID=s1.ProductID
AND s1.LogID=MAX(s.LogID))
FROM StockLog s
GROUP BY ProductID)
For each group — remember that each group represents one
product — this subquery finds the most recent log entry. The most
recent log entry is determined by looking at the LogID column. If the
event is not a delete event, we include its LogID identifier in the
result set. If the event is a delete event, a 0 is returned instead
of the LogID value. When the StockLog table was first created, the
LogID column was defined using the IDENTITY keyword. That means it
begins with 1 and increments by 1. Since no log record has an ID of
0, our snapshot query will not return any data for log records
representing deletes.
Since we are generating a snapshot, GROUP BY is used to find only the
last event for each product. See the highlighted code in the
following example. All those products that were deleted from the
Stock table have their last events marked by a LogId of 0. Only the
ones with valid, nonzero, LogIds are used to generate the snapshot:
SELECT ProductID, Qty
FROM StockLog
WHERE LogId IN
(SELECT
(SELECT (CASE WHEN MAX(Type)!='D'
THEN MAX(s.LogID)
ELSE 0 END)
FROM StockLog s1
WHERE s.ProductID=s1.ProductID
AND s1.LogID=MAX(s.LogID))
FROM StockLog s
GROUP BY ProductID)
The list of LogId values generated by the subquery is used by the
main query as a basis for selecting the most recent ProductID and Qty
value for each product.
The query in this recipe is quite complex and may take a long time to
execute. Because of that, it is not suitable for use in reports that
run frequently. For occasional reporting, however, this
can be quite useful.
|