Use UPDATE triggers to maintain data integrity

This week's tip is totally specific to SQL server.

I have the following two tables in my database:

Table:  Country
Field # Name Type
Field1: CountryID varchar(3) primary key
Field2: CountryName varchar(50)

Table:  StateProvince
Field # Name Type
Field1: ProvinceID varchar(3) primary key
Field2: CountryID varchar(3)
Field3: ProvinceName varchar(50)
Field4: CountryName varchar(50)

The CountryName field in the StateProvince table is redundant, and must be updated whenever the CountryName field is updated in the Country table.

For the sake of this example, it is a requirement that the structure of these two tables is not altered, because there is a large application which interacts with this database that would cost $$$$ to alter.

One way to address this problem, is to write an UPDATE trigger like the following:

CREATE TRIGGER UpdateCountry ON Country
FOR UPDATE
AS
BEGIN
    DECLARE @sOldCountryName VARCHAR(50)
    DECLARE @sNewCountryName VARCHAR(50)

    SELECT @sOldCountryName = CountryName FROM DELETED
    SELECT @sNewCountryName = CountryName FROM INSERTED

    UPDATE StateProvince
      SET CountryName=@sNewCountryName
    WHERE CountryName=@sOldCountryName
END

Well, let's see if it works.

I have the following records in my Country table:

CountryID CountryName
AU Australia
NZ New Zealand
US United States of Amerrica

...and the following records in my StateProvince table:

ProvinceID CountryID ProvinceName CountryName
QLD AU Queensland Australia
VIC AU Victoria Australia
PA US Pennsylvania United States of Amerrica
OR US Oregon United States of Amerrica

Now, I couldn't help noticing that the name of one of the countries on my list, has been misspelled, so I'd better update it. Most normal people would probably do that through the enterprise manager, but I'll do it by executing the following SQL command:

UPDATE Country
SET CountryName='United States of Amerrica'
WHERE CountryID='US' 

The trigger I wrote has correctly updated my StateProvince table so that it contains:

ProvinceID CountryID ProvinceName CountryName
QLD AU Queensland Australia
VIC AU Victoria Australia
PA US Pennsylvania United States of America
OR US Oregon United States of America

So far, so good.

Now, it turns out that I have several databases which store information on different countries, and I have developed a standard set of names for each country. So that for example, "China" is referred to as "The People's Republic of China".

I have put this standard list of country names in a table called StandardCountryNames:

CountryID CountryName
AU People's Republic of Australia
US The United States Of America

To update my Country table with these names, I have executed the following query:

UPDATE Country
SET CountryName=StandardCountryNames.CountryName
FROM Country INNER JOIN StandardCountryNames
ON Country.CountryID=StandardCountryNames.CountryID

This UPDATE, has updated two records in the Country table, so that it now contains:

CountryID CountryName
AU People's Republic of Australia
NZ New Zealand
US The United States Of America

The StateProvince table now contains:

ProvinceID CountryID ProvinceName CountryName
QLD AU Queensland Australia
VIC AU Victoria Australia
PA US Pennsylvania The United States Of America
OR US Oregon The United States Of America

If you are skimming through this article, look at the above table again. Of the four records shown, two have been updated, and two have not.

Why?

The UPDATE trigger, is only executed once per UPDATE command. It is not executed for each individual record that is updated.

The second version of my UPDATE trigger is as follows:

CREATE TRIGGER UpdateCountry ON Country
FOR UPDATE
AS
BEGIN
    UPDATE StateProvince
      SET CountryName=INSERTED.CountryName
    FROM StateProvince INNER JOIN INSERTED
      ON StateProvince.CountryID=Inserted.CountryID
END

This trigger will work correctly, if the CountryID field is not being updated.

If the CountryID field is being updated, the JOIN condition must be ON some other unique field that is not being updated.

If the CountryName field is unique, and if it will never be updated at the same time as the CountryID field, then the following trigger will check whether the CountryID field is being updated and act accordingly:

CREATE TRIGGER UpdateCountry ON Country
FOR UPDATE
AS
BEGIN
    IF Update(CountryID)
        UPDATE StateProvince
          SET CountryID=INSERTED.CountryID
        FROM StateProvince INNER JOIN INSERTED
          ON StateProvince.CountryName=Inserted.CountryName
    ELSE
        UPDATE StateProvince
          SET CountryName=INSERTED.CountryName
        FROM StateProvince INNER JOIN INSERTED
          ON StateProvince.CountryID=Inserted.CountryID
END

What if the CountryID field is updated at exactly the same time as the CountryName field, as in the following UPDATE command?

UPDATE Country
SET CountryID='AUS',
CountryName='The great proud land of Australia'
WHERE CountryID='AU'

This represents a problem which cannot be solved without modifying the structure of the database, because within the trigger, there is no way of linking the field values on the updated record with the previous field values.

The only way to accommodate this possibility, is to add another unique key, one that will never be updated.

...And that is why it is nearly always a good
idea for every table to have an IDENTITY field.

Adelle.

 

Home About the Author Copyright © 2001 Adelle Hartley