views:

1057

answers:

7

I recently accidently wrote a really ugly stored proc where I wished I had enums,

Eg.

CREATE PROCEDURE Proc_search_with_enum @user int, @account_category {enum}

I understand that SQL 2000 doesn't have enums as a first class language construct, which coding conventions do you use to simulate enums or otherwise address the same issue?

Or am I'm I doomed to just using VARCHAR and IF @account_category='cat1'?

EDIT: T-SQL and C# are the client languages.

EDIT: Thanks all! Lots of good advice, I wish I could accept several answers, I've voted everyone up-

Summary of answers

  • Lean on C#'s enum by using int. Good for C# client code, makes TSQL client code less readable.
  • Use Char/Varchar. Bad for C# client code (not good for localization), makes TSQL code more readable.
  • Use parameter checking code to restrict the parameter, or use a constraint on a table column or a foreign key if the parameter is going to be inserted into a table.
+6  A: 

You could take a look at the answer to this question. As far as I know, enum types are not part of SQL Server.

Anyhow, it's better to use an integral (INT, TINYINT, ...) type for your enums than a string type. Usually the enum types in your programming language of choice correspond better to integers than to strings.

In C#, by default every enum value corresponds to an integer, starting from 0. You can even cast between them, so this is valid code:

public enum MyEnum
{
    FirstEnumValue,
    SecondEnumValue
}

...

// Assuming you have opened a SqlDataReader.
MyEnum enumValue = (MyEnum) reader["account_category"];

And LINQtoSQL also supports this. If you have an enum-typed property and your database column is an integer type, conversion is automatic.

Ronald Wildenberg
If you go with this technique then it's probably a good idea to explicitly specify the underlying values for the enum to ensure that any future updates don't accidentally break the correlation between your C# code and the database values. For example: public enum MyEnum { FirstEnumValue = 1, SecondEnumValue = 2 } etc
LukeH
Agreed, I usually take the default values and add new values at the end but making it explicit is probably a good idea.
Ronald Wildenberg
+1  A: 

In PostgreSql I just use VARCHARs with constraints attached ...

CREATE TABLE movie_clip (        
    type VARCHAR(40) NULL CHECK(type IN ('trailer', 'commercial')),
);

#=> insert into movie_clip (type) values ('trailer');
INSERT 0 1
#=> insert into movie_clip (type) values ('invalid value');
ERROR:  new row for relation "movie_clip" violates check constraint "movie_clip_type_check"

#=> \d movie_clip
....
"movie_clip_type_check" CHECK (type::text = ANY (ARRAY['trailer'::character varying, 'commercial'::character varying]::text[]))

I don't like using numeric types for simulating ENUMs, because they are not descriptive enough. With the above schema I can see right away what possible values it receives, and I also get the meaning of those values right away. I also get type-safety, since I can't insert invalid values in that column.

For more details on constraints for Postgres, see here: http://www.postgresql.org/docs/8.1/static/ddl-constraints.html

Alexandru Nedelcu
+1  A: 

Sometimes CHAR type is more usable than INT - fixed size char doesn't take much storage room and you can see "enumerated" values directly in database fields. No difference from code side, but big advance while working directly with SQL tools.

Arvo
+2  A: 

I generally prefer to call the parameter @account_category_code and make it a CHAR(3), if there are only a few enum values and they can all be expressed cleanly with three letters. Then the domain is enforced with a check constraint. If there are more than a handful of values (more than around 4 or 5) then I would generally switch to a tinyint/smallint called @account_category_type_id and have a domain table to reference for it. We don't have a hard and fast rule in my organization, but I find this works well.

+1  A: 

Be exteremely careful in using CHAR type for enum, and avoid it at all if you want your app/db go internationally.
Don't confuse the DATA with it's presentation: like the words should suggest, an enum(eration) is a number, it's description is a totally different business. To make it short, using a CHAR variable/field for an enum, you tie yourself to a particular language for their description: you can forget about internazionalization, for instance. Can you imagine what the word "Weltmeisterschaft" means, in which language and - moreover - how many different ways of writing it wrong there can be? Actually, a (tiny)int has actually the downside of values not intrisecally auto-descriptive: I didn't say it's the perfect solution!

Turro
IMHO the "code" (account_category_code) of an enum should never be localized. This would be analogous to localizing the application source code that defined a c#/c++ enum. Instead you should haven account_category_type_desc (by our nomenclature) that is the human displayed enum description, this is what you would localize.
1) You're not going to let your end user play with your source code (but you can easily define different translations for the enum) 2)If you let your end user use codes, you are going into troubles: they may not know your language or, even if they know, may deliberately want not to use it: keep in mind that if they have access to the db they can manually insert values into. 3) If you use codes instead of description, things are even worst: what is "WMS" is as obscure as an int, as you have to guess which language on earth (literally) is, and what does this mean.
Turro
4) You run the risk of people (or external files - keep also in mind external sources out of your control) inserting illegal values : this could be easily avoided, though, with good data validation before inserting into the table or using into the proc/function. I agree that is not a perfect solution, but in my experience (18 years) when working with people of different cultures and languages use of CHAR keys for tables and enums can hide so many little problems I never thought of before. That's my advice, though, and I don't think I'll never come into your office refactoring your code/dbs :)
Turro
@Turro, are we in violent agreement? :) I agree an enum description would be a great candidate to localize. But think again of app code - you wouldn't create a second enum with the same behind int values as the first to have a localized drop down. Instead you have resource files (or the like) that contain the translation for each enum value. See my 'answer' in this thread to see the very restricted cases I use a CHAR based enum for. The code is the PK, you can just as easily define a localized table later (if needed) based on the natural CHAR PK rather than an artifical identity key.
"Violent agreement"? I could like it! Joking apart, no, maybe I didn't mean at all to be violent or harsh: _I_do_apologize_ if I made you think that. I simply wanted to justify my point: I'm currently writing an application which runs in many countries (Europe and Africa now, Asia soon) and I've run in MANY troubles with localizing, in general, not only enums. In my experience (maybe limited) a number for an enum can be a easy (odd but true) common base, instead of strings. Almost all of my enums are also saved in _tables_: I found easier to test enum=1 rather than enum="Бз". I owe u a beer :)
Turro
Aargh: there's a maybe too much in the first sentence: I didn't mean at all to be violent or harsh: I owe you TWO beers, now!
Turro
+1  A: 

In SQL, you enumerate items by putting them in a table. Create a lookup table for your account categories and have this parameter accept the primary key for the new table.

Joel Coehoorn
+2  A: 

You could create a view to simulate the Enum. See this article http://www.olegsych.com/2008/07/t4-template-for-generating-sql-view-from-csharp-enumeration/

Vasu Balakrishnan