views:

27

answers:

1

I'm looking for a way to non-destructively alter data. I have one table that has data that shouldn't be edited - it is the master data. I would like to have user edits on this data, but in order to maintain the integrity of the master data I've created a second table that is a mirror of the structure of the master table.

My thinking is that I basically would make a view (vAdjustedData) such that this data is UNIONed together giving me a source that was the most up-to-date version of the data. (The purpose of this is to allow users to undo their data in the adjusted table and have the master tables data reverted to be current).

Columns in the tables will be nullable. The idea is that when a user wishes to make an edit that at that time the master record will be copied, changes applied, and then saved to the adjustments table. My idea for a UNION would mask the original master record with the adjusted record such that when placed in a view I will get the "current" data. When this adjustment is deleted the view will "automatically" roll back.

So I have Table_Master and Table_Adjusted. Table_Master has a primary key. Table_Adjusted also has a primary key, but it is a foreign key to the primary key of Table_Master. If both tables had a varchar column I would expect to write a view that was something similar to this:

(SELECT ID, Value
FROM Table_Adjusted)
UNION
(SELECT ID, Value
FROM Table_Master
WHERE ID NOT IN (SELECT ID FROM Table_Adjusted))

The UNION above should bring all the adjusted values and then all of the values from the master that did not have an adjustment record.

Is this the correct way to do this? To me it seems inefficient. I thought an EXCEPT would work (using SQL2K8) but it didn't seem appropriate.

+2  A: 

I'm assuming that you want the adjusted table to only adjust values and not add or remove values. Your query has a disadvantage that it is in theory possible to add new values which don't even exist in the original table. This version prevents this possible risk:

SELECT Table_Master.ID,
    CASE WHEN Table_Adjusted.ID IS NULL THEN Table_Master.Value
         ELSE Table_Adjusted.Value
    END AS Value
FROM Table_Master
LEFT JOIN Table_Adjusted
ON Table_Master.ID = Table_Adjusted.ID

Also your query uses a UNION DISTINCT but really you only need a UNION ALL. This slightly modified version of your query should run slightly faster:

SELECT ID, Value
FROM Table_Adjusted
UNION ALL
SELECT ID, Value
FROM Table_Master
WHERE ID NOT IN (SELECT ID FROM Table_Adjusted)
Mark Byers
I had thought of using the left join and doing CASE WHENs, but I figured that the UNION would produce faster results. Unfortunately, I have nullable fields. The adjusted table would only alter values that would appear in the master table.
Mike G
@Mike G: My second query does handle the case where the Value is nullable. I'll update my answer to remove the first query to avoid confusion.
Mark Byers
Now that I think about it wouldn't it be a LEFT OUTER JOIN? That way the tuples returned would be the value and then a null. For the nullable columns I could use the case check and for non-nullables the COALESCE operator. However, my real world table has a lot of columns from denormalization (to eliminate joins) and the amount of adjustments is not thought to be large.
Mike G
@Mike G: A `LEFT JOIN` and `LEFT OUTER JOIN` are exactly the same thing, so yes.
Mark Byers
@Mike G: I've added a new suggestion which is actually just a slight modification of your original query- All I did was add `ALL`, but I think it will give you a slight speedup.
Mark Byers