Question

I am looking for some sort of documentation on why this is (or is not) a poor design.

A 3rd party vedor's code uses a separate table to store the primary key of all tables.

__KeyTable____
NextKey  TableName
45       Table1
1003     Table2
90       Table3

__Table1____
id  Column1  Column2
45  ...      ...

CREATE TABLE [dbo].[Table1](
    [id] [int] NOT NULL,        /* <---- IDENTITY(1,1) is not present */
    [Column1] [nvarchar](1) NULL,
    [Column2] [nvarchar](1) NULL
 CONSTRAINT [PK_WFDATASET] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)

So when a new record is inserted into Table1, KeyTable is queried, the number 45 is found, 1 is added to that (in C# on a different server). The number is used in the next insert statement of Table1 then KeyTable's Table1 record is updated to 46.

This also prevents the ability to use cascading deletes. Instead every table has around 3 triggers which duplicate the effect of a cascading delete. It's an ugly design, I'm looking for some official article I can point at to explain why this is a terrible design.

Was it helpful?

Solution

Well, it's a terrible design because you need to lock a row in KeyTable for the duration of your transaction against Table1. That is, while you're running the transaction:

  1. Fetch the value from KeyTable
  2. Store the incremented value in KeyTable
  3. Insert a new row in Table1

No concurrent session can be doing the same. This is a big penalty for scalability, because all your sessions that insert to Table1 are forced to queue up and access KeyTable serially.

Whereas any well-implemented identity feature should only need to lock the identity-allocator very briefly, improving throughput. This happens in memory. IDENTITY caches a set of the next 10 identity values, and sessions that insert rows draw from this set.

Using IDENTITY should reduce contention for locks on the storage engine level too, which would be required while writing the changes in KeyTable back to disk.

OTHER TIPS

A side-effect of using IDENTITY and its workalikes is that gaps in the sequence of values might happen. This is not a bug; it's by design.

But some applications require that you account for every ID number you use. Think about financial management systems, where you might need to account for every invoice number or every check number from the beginning of time to the end of time. Using a key table, like the one in your example, is one way of guaranteeing there are no gaps in the sequence of values.

Another possibility is that your application grew up from an older, simpler system that wasn't based on a SQL dbms. (And so didn't have anything like IDENTITY.)

The only straightforward reason I can think of that your application would allow deleting rows (which might introduce a gap) without also using ON DELETE CASCADE would be that your tables don't have foreign keys. There's no technical conflict between using a key table and using foreign keys declared with ON DELETE CASCADE. (Try it.)

One rule of thumb for database designers is "Don't replace DDL with procedural code," which would mean that you wouldn't normally write three triggers on each table when you could just declare the foreign keys as ON DELETE CASCADE. There are rare exceptions; chances are this isn't one of them.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top