user30943
user30943

Reputation: 61

Database Design - Hard Coded Row ID

What's everyone take on tieing code enum's to row ID's in a database table? I'm really looking for a cleaner alternative. What if for example your static rows in a given table are ID's 1,2,3, and then that table gets filled with user transactional data using ID's 4-100, and then you later want to add a new row ID which in your local production database is row ID 4, but when that row goes to a customers database it will have to be 101...well that kinda breaks everything.

So how do you handle static locked rows in a table that also gets filled with transactional data?

Thanks, MeshMan

Upvotes: 5

Views: 2310

Answers (10)

GenericJoe
GenericJoe

Reputation: 27

I agree with Stephen W. that a good solution is to use the string name of the ENUM item. I use this method all the time in .Net and it is easy to use.

The idea is that regardless of what value your enum's have (and regardless of what ID's the database records have) the enum name will only change if your developers change it. Compared to an automatically incrementing ID number (out of your control) this is a much more manageable situation. Also other developers reading your code know what a name is trying to describe, as opposed to some apparently random number which could mean anything, and could very likely mean multiple things across different databases!

One way this could work, is for your table to have a text code or description identity column. This could be an alpha-numeric value, but the key is that it should uniquely identify that record. In reality this could be used as a primary key, but in practice I always have an auto-incrementing primary ID number column anyway. Example of columns on such a table:

PK_ID | CODE | Other Data etc.

Here, the value of the code field is actually your Enum name. This is a code that should not need to change. You must lay down the rule amongst yourself and your team that these DON'T CHANGE, but if somebody needs to change it then make sure it is reflected on both platforms.

Using .Net to work with an Enum (in this example one called SortDirection):

' Get the string name of an enum
[Enum].GetName(GetType(SortDirection), SortDirection.Ascending)

' Get the enum value from its string name
CType([Enum].Parse(GetType(SortDirection), "Ascending"), SortDirection)

Its not a perfect solution but it has served me well; I have been confused and irritated by hard coded ID's too many times to not appreciate the better of these two evils. Hope that helps!

Upvotes: 1

WW.
WW.

Reputation: 24291

I have had to do this before on a status table. There were certain "in-built" values that must always exist when the application is installed and are treated specially at certain points in time.

Then, there was the ability for users to create their own for workflow reasons.

What you could do is start your sequence at 1,001. Any values between 1 and 1,000 are "inbuilt" values. Of course, 1000 is a magic number here, but in my case I only had 5 inbuilt values so it seemed pretty safe.

The other option would be ids greater than zero are user created and less than zero are system inbuilt.

Upvotes: 0

Mark Brady
Mark Brady

Reputation:

Not judging the rationality of your plan. DBA's and other people always like to say, " don't do that." as if you asked if you should do it or not, and even if you didn't ask that, like you have a choice to not do it.

I will assume that you asked your question for a reason and that you don't have a choice to not do this.

I have a friend who thinks that Oracle sequences are a pain and that autonumber fields are so much easier. They aren't as "easy" but they are much more flexible. To this point you haven't stated your platform, so no mod -1 please.

In Oracle you create sequences to fill in autonumber fields. Sequences are completely independent from tables. You can use one sequence in multiple tables, you can use multiple sequences in one table. If I had to solve your situation, I'd create 2 sequences one that starts at 1 and steps by 2 (for admin data) and one that starts at 2 and steps by 2 (for user data). I'd change my insert procedure to have an option parameter for admin inserts to pull the correct sequence. you'll never have collisions of your data and your users data. There's a side effect of being able to distinguish between the two based on the parity of the ID. The downside is that there's a hard limit.

Upvotes: 0

Noah Yetter
Noah Yetter

Reputation: 396

ROWIDs should never be stored because they can change. If you moved one of these tables to a new tablespace all of your stored ROWIDs would become invalid (this is true in Oracle, anyway).

If you have a special row for FK purposes that needs to be protected you can use a trigger to prevent it from being updated.

Upvotes: 0

Bill Karwin
Bill Karwin

Reputation: 562348

Here's a solution: most databases have a mechanism to generate new ID values that start at 1 and increment in the positive direction.

But if the primary key column is a signed integer, you can use negative values for your static rows. ID of -1 means '--NONE--', ID of -2 means '--SPECIAL--' or whatever. If you need more static rows, advance in the negative direction.

Then your foreign keys can still reference both static and user-generated rows, if the foreign key is also a signed integer.

Upvotes: 0

BradC
BradC

Reputation: 39946

Don't base special logic in your application on Row IDs in your database, especially if you don't have absolute control of what will be in that table. (I admit I sometimes do this for lookup tables that I absolutely know will not be changing, but even here it is probably bad practice.)

If you need to flag certain special records, then put some kind of "flag" field that indicates that, and query on that flag instead.

Upvotes: 2

Stephen Wrighton
Stephen Wrighton

Reputation: 37819

Well, an enum should be a constant--something you're not expecting to change--which you really can't say about data from the DB.

Anyways, you're problem comes from the fact that you're saving/loading from the INTEGER behind the text of the ENUM. A solution which will help here, would be to do lookups/saves/etc from the TEXT value of the enum

This VB code translates a string back to an ENUM, and it should be easy enough to port to C#:

[Enum].Parse(System.Type, Value)

Upvotes: 0

Steven A. Lowe
Steven A. Lowe

Reputation: 61233

i agree with Ken G - having enum values correspond to row IDs only makes sense for lookup tables with static (unchanging) content

Upvotes: 1

Ed Altorfer
Ed Altorfer

Reputation: 4371

Maybe you could post an example to help us better understand, but if you have some rows that are designated as "system" entries, you could consider adding a column to the table called Order and then, when you create an entry that is designed to be the system entry, you can increment the Order column.

For most other things, like lookup tables, you should be able to control the row IDs more explicitly and use them.

Upvotes: 0

Ken Gentle
Ken Gentle

Reputation: 13357

Don't do that. ;-)

If you have static rows, values that never change, in a table that has user data that is transactional or at least mutable, then I'd say you have at least a normalization issue in the schema.

Reference data belongs in it's own table, usually. If the table itself contains only the reference data, then assigning the IDs from the application or using generated IDs from the DB becomes a matter of preference.

I've often toyed with the idea of generating either a 'source code' Enum class from DB tables or filling DB tables with Enum class information at build/deployment time, but I've never 'gotten around to it'.

Upvotes: 12

Related Questions