Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
mdhingra01
Posting Yak Master
179 Posts |
Posted - 2004-09-20 : 12:52:16
|
| I want to build a lookup table for codes. The structure of my table is:CodeTypeCodeEnglishFrenchCodetype will identify what category of the code.Code will be a numeric value.English and French will be descriptions of the code.I would like to have CodeType and Code set as a Key so that every code is unique per Codetype. In other words.... Codetype A could have codes 1,2,3,4 associtaed with it and Codetype B will have its own set of codes 1,2,3,4... etc.In trying to build this table, I am setting the Code column as an identity column starting at 1 and incrimenting by 1. This works fine for the first codetype, as soon as I start loading codetype B intot the table, it continues in the numeric sequence from where it left off from Codetype A. I want to Start again at code =1 for Codetype B and the same for Codetype C etc...Is there an easy way to do this. Some codetypes have 400+ codes and I don't want to manually have to go through each one setting the code.Thanks. |
|
|
rockmoose
SQL Natt Alfen
3279 Posts |
Posted - 2004-09-20 : 15:11:57
|
| I can't recommend that you take the "code table" approach.It will be harder to maintain dataintegrity, and it will be slower, and it will be more complicated to query.The only thing that you accomplish is to reduce the number of tables, but this is not an improvement of the database design at all,it may "look" simpler, but it isn't.I suggest you have one separate table for each code.rockmoose/* Chaos is the nature of things...Order is a lesser state of chaos */ |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2004-09-21 : 12:23:31
|
| FWIW we have a number of tables that are reused ... "Validation" - Column name, Value and Description - used for columns that have constrained / picklist type values. Fixed by the developer."Select list" - same again, but the client is allowed to change these."Notes" - Table name, PK value, Notes text. Allows storing of "general notes information" - e.g. by support staff against a customer account etc. Also used by application - e.g. "user requested forgotten password"We are able to roll out new picklists etc. without also having to roll out a new table, referential integrity, etc. So we can just tick a box in our Dev. stuff saying "This column is now a picklist" and then thing that does the form fields on HTML form pages just makes that field into a <SELECT> list instead of an <INPUT>But I do understand the argument for one-per-use.For mdhingra01 to use a single table and have CodeType B allocate the next available number a trigger could be used that allocates a vlue when the column is, say, NULL (or "-1" or somesuch) by doing a SELECT MAX(CodeValueColumn)+1 FROM MyCodeTable WHERE CodeType = 'B' ...Kristen |
 |
|
|
rockmoose
SQL Natt Alfen
3279 Posts |
Posted - 2004-09-21 : 15:53:46
|
I like your work Kristen, very structured and functional.But we have left relationland here and we are losing some fundamental concepts such as dataintegrity and normalisation.What you are doing here is a tradeoff between a desired functionality and dataintegrity.This is a design choice, and if this design works better than another, sure.I would argue that a normalized design in a sql database that can maintain dataintegrity is practical and sound,but I would not go so far as to say that it is the best design always.mdhingra01, think twice before putting all your codes in a single lookuptable, this design might turn out to be "not so practical" after all.---------------------------------------------------------------This lookuptable might look good to some programmers out there:create table #domains(domainname varchar(20) not null primary key, domaindefinition varchar(256) not null, domainvalues text null)insert #domainsselect 'sqltypes', '.....', '<domainroot><value>bigint</value><value>binary</value><value>bit</value><value>char</value><value>datetime</value><value>decimal</value></domainroot>'select domainname, domainvalues from #domains where domainname = 'sqltypes'drop table #domains /rockmoose |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2004-09-22 : 13:25:13
|
| Oh, I do agree with you. Just I prefer not to have a swath of tables each storing a couple of rows just to satisfy a picklist that, say, only the programmers can add new values to.A country code lookup table that has Code and Description columns, but might have Carriage Rate, Lanugage, Currency symbol etc. added in the future, definitly gets its own table here.But what issue with "dataintegrity" do you have?Kristen |
 |
|
|
rockmoose
SQL Natt Alfen
3279 Posts |
Posted - 2004-11-03 : 04:05:36
|
| Since I reference this post from other thread, and I have had some conscience not answering yet...[url]http://sqlteam.com/forums/topic.asp?TOPIC_ID=42063[/url]The data integrity is the following:1. notes table: "Notes" - Table name, PK value, Notes textOk maybe it is not so important if rogue records exist in this table, but with this construct, you cannot ensure that all the notes reference a record in a table. ( no FK constraint )2. "Validation" - Column name, Value and DescriptionThis is a defined domain for a specific column, but if this is the only table constraining the values of a particular column, it will be possible to enter an invalid value in a column. ( since no FK constraint can be put between valid values and column )quick response, I am sure there is much more to discuss...rockmoose |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2004-11-03 : 13:56:11
|
| "Since I reference this post from other thread, and I have had some conscience not answering yet"Say three hail mary's and forget about it!Code is pretty tight here - not much opportunity for users to open the table in an Access grid and just start bashing some rows in!Our SProcs and (to some extent) our Triggers worry about such stuff. For me its been a dissapointmment that RI doesn't allow for conditional stuff. Our RI is based on stuff we wrote (for our own App. only) back in the '70's that allowed for conditional RI. SO we had RI defined along the lines of:Delete if no children.Delete all children too, regardless.Delete all children, but only after warnign, and getting confirmation, from the operator.We also had:FK is Master.PK joined to Child.PK; plus Child.Item is an incremeted column - the numbers were 1-based for each Child.PK.Then we had FK definition is Master.PK joined to Child.PK plus Child.Attribute = 'XXX' - thi si how we did the Notes thingie. Our "RI" would definte the value for the Attibute when joining to a particular table - so when joined to customers it would be "CUST", and suppliers would be "SUPP" and so on.It doesn't work in the Real World of SQL as I now know it - but it solved a load of application building problems we had at the time.Kristen |
 |
|
|
rockmoose
SQL Natt Alfen
3279 Posts |
Posted - 2004-11-03 : 14:39:12
|
  RI as implemented in current DBMS is not "flexible" as to allow for such conditional constraints.I doubt it ever will. There are always triggers if one has to enforce more complex RI.The physical design can differ from the logical design for variuos reasons.Although it is sound to question designs that break RI and normalization,IMO there has to be sound and conscious design decisions behind any such implementation.As if you weren't aware of that... rockmoose |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2004-11-03 : 14:47:10
|
| Those hail marys need a hair cut!Kristen |
 |
|
|
rockmoose
SQL Natt Alfen
3279 Posts |
Posted - 2004-11-03 : 15:10:15
|
| Sure do,whish it were friday btwrockmoose |
 |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
Posted - 2004-11-03 : 15:11:00
|
UDF's do allow you to handle RI for this type of lookups, if you wish to store them all in 1 table.Take a look at this, for some ideas:-- Each lookup list gets 1 entry here (the header table)create table LookupLists (ListID int primary key, ListDesc varchar(100))-- All lookup values for each list are stored here (details)create table LookupListValues (ListID int references LookupLists (ListID), Code varchar(10) not null, CodeDesc varchar(100) not null,primary key (ListID, Code))go-- some sample data in our lookup tables:insert into LookupLists (ListID, ListDesc)select 1, 'Status Codes' unionselect 2, 'Priority Codes'insert into LookupListValues (ListID, Code, CodeDesc)select 1,'A','Active' unionselect 1,'I','Inactive' unionselect 2,'H','High' unionselect 2,'M','Medium' unionselect 2,'L','Low'-- we can use the UDF technique to write our CHECK constraint:gocreate function CheckLookup(@ListID int, @Code varchar(10))returns bitasbegin return case when exists (select * from LookupListValues where ListID= @ListID and @Code = Code) then 1 else 0 endend-- Now, we can use some tricks to allow for RI on columns in particular tables-- to reference a particular Lookup list, like this:gocreate table StuffWithStatuses (ID int primary key, Col1 varchar(10), StatusCode varchar(10) check (dbo.CheckLookup(1,StatusCode)=1))gocreate table StuffWithPriorities (ID int primary key, Col1 varchar(10), PriorityCode varchar(10) check (dbo.CheckLookup(2,PriorityCode)=1))go-- INSERT OF STATUS CODE LOOKUP VALUES: -- will work ok:insert into StuffWithStatuses (ID, Col1, StatusCode)select 1,'Test1','A' unionselect 2,'Test2','I'-- will NOT work ok:insert into StuffWithStatuses (ID, Col1, StatusCode)select 3,'Test3','H' unionselect 4,'Test4','X'select * from StuffWithStatuses---- INSERT OF PRIORTY CODE LOOKUP VALUES: -- will work ok:insert into StuffWithPriorities (ID, Col1, PriorityCode)select 1,'Test1','H' unionselect 2,'Test2','M' unionselect 3,'Test3','L'-- will NOT work ok:insert into StuffWithPriorities (ID, Col1, PriorityCode)select 4,'Test4','A' unionselect 5,'Test5','X'select * from StuffWithPrioritiesgodrop table StuffWithStatusesdrop table StuffWithPrioritiesdrop function CheckLookupdrop table LookupListValuesdrop table LookupListsgo - Jeff |
 |
|
|
rockmoose
SQL Natt Alfen
3279 Posts |
Posted - 2004-11-03 : 15:44:31
|
Nice angle,it will not constrain deletes from the LookupListValues though.So it is still possible to bypass RI and screw up the database.It makes sure no shit is entered in any case rockmoose |
 |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
Posted - 2004-11-03 : 16:01:47
|
>>it will not constrain deletes from the LookupListValues though.yes -- that is correct. one way you can do that, which I am not sure i recommend or not, is by adding "dummy columns" to your table. I had hoped a computed column would suffice, but I cannot seem to create a foreign key constraint on a computed column. SO i used a column with one 1 possible value via a check constriant, and a default of that same value. (kind of like a computed column, but the value is actually stored).So, this is more of a true "RI" implementation, w/o using check constraints:-- Now, we can use some tricks to allow for RI on columns in particular tables-- to reference a particular Lookup list, like this:gocreate table StuffWithStatuses (ID int primary key, Col1 varchar(10), StatusCode varchar(10), Dummy int default (1) check (dummy=1),constraint sws_fk foreign key (Dummy,StatusCode) references LookupListValues (ListID, Code))gocreate table StuffWithPriorities (ID int primary key, Col1 varchar(10), PriorityCode varchar(10), Dummy int default (2) check (dummy=2),constraint swp_fk foreign key (Dummy,PriorityCode) references LookupListValues (ListID, Code))go-- INSERT OF STATUS CODE LOOKUP VALUES: -- will work ok:insert into StuffWithStatuses (ID, Col1, StatusCode)select 1,'Test1','A' unionselect 2,'Test2','I'-- will NOT work ok:insert into StuffWithStatuses (ID, Col1, StatusCode)select 3,'Test3','H' unionselect 4,'Test4','X'select * from StuffWithStatuses---- INSERT OF PRIORTY CODE LOOKUP VALUES: -- will work ok:insert into StuffWithPriorities (ID, Col1, PriorityCode)select 1,'Test1','H' unionselect 2,'Test2','M' unionselect 3,'Test3','L'-- will NOT work ok:insert into StuffWithPriorities (ID, Col1, PriorityCode)select 4,'Test4','A' unionselect 5,'Test5','X'select * from StuffWithPriorities-- and, now, this should fail:delete from LookupListValueswhere ListID = 2 and Code = 'M'godrop table StuffWithStatusesdrop table StuffWithPrioritiesdrop table LookupListValuesdrop table LookupListsgo Note the "dummy" columns in each of the two tables. I didn't know that Dummy is aparently a keyword in SQL Server (it turns blue in QA).- Jeff |
 |
|
|
rockmoose
SQL Natt Alfen
3279 Posts |
Posted - 2004-11-03 : 17:26:11
|
If we combine your 2 ideas we could create Domain + DomainValues tables( same as lookup + lookupvalues tables )Where we1. use a UDF to perform a check constraint for the domain values2. use the "dummy" column to ensure RI for any table having a column referencing a domaindoesn't look too bad for me IAC.-- The domainscreate table Domains( DomainID int primary key, DomainDesc varchar(100))go-- The domain constraintscreate function fnCheckDomainValue(@DomainID int, @Value varchar(10))returns bitasbegin if @DomainID = 1 -- domain 1 only numeric 1-100 begin if isnumeric(@Value) = 1 and @Value between 1 and 100 return 1 end else if @DomainID = 2 -- domain 2 only 3 pos char code begin if len(@Value) = 3 and @Value like('[a-z,A-Z][a-z,A-Z][a-z,A-Z]') return 1 end return 0endgo-- The domain valuescreate table DomainValues( DomainID int references Domains(DomainID), Value varchar(10) not null, ValueDesc varchar(100) not null, primary key(DomainID,Value), constraint chkValidDomainValue check( 1 = dbo.fnCheckDomainValue(DomainID,Value) ))go-- A table that references Domain # 1create table RefDomain1( col varchar(10), MyDomain int default 1 check(MyDomain=1), MyValue varchar(10), constraint FK_MyDomain foreign key(MyDomain,MyValue) references DomainValues(DomainID,Value))go-- insert sample domainsinsert Domains(DomainID,DomainDesc)select 1,'1-100 domain' union all select 2,'3 pos char domain'-- insert sample domain values-- okinsert DomainValues(DomainID,Value,ValueDesc) select 1,1,'...'-- not okinsert DomainValues(DomainID,Value,ValueDesc) select 1,101,'...'-- not okinsert DomainValues(DomainID,Value,ValueDesc) select 1,'g','...'-- okinsert DomainValues(DomainID,Value,ValueDesc) select 2,'abc','...'-- not okinsert DomainValues(DomainID,Value,ValueDesc) select 2,'000','...'-- not okinsert DomainValues(DomainID,Value,ValueDesc) select 2,'g','...'-- insert table referrencing a domain-- okinsert RefDomain1(col,MyDomain,MyValue)values('a',default,1) -- btw the default constrains the inserted value to 1 :)-- not okinsert RefDomain1(col,MyValue)select 'a',2select * from Domainsselect * from DomainValuesselect * from RefDomain1-- cleanupdrop table RefDomain1drop table DomainValuesdrop table Domainsdrop function fnCheckDomainValuerockmoose |
 |
|
|
|
|
|
|
|