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 |
|
nic
Posting Yak Master
209 Posts |
Posted - 2003-01-17 : 12:37:59
|
| Hi,I'm build an program/database that will track "user applications". These applications will have different statuses. (complete,incomplete,finaled,etc). These values are stored in the database and I need to references these statuses throughout the program. Here is the dilema. I can see a scenario where a "user" might not like the name of a status and would like to change it. That being said, I can't reference a value in code that could be changed. The primary key for a status is an int value and in code, it makes it difficult if I reference status '1' which correlates to 'Incomplete'. Sooooo I made a the status table to be like:Create table tblStatus( st_id int ,st_systemName varchar(20) ,st_displayName varchar(20))The idea being, in code I can reference the systemName which will never change. If the user wants to change the display name it won't affect anything. By referencing in code, the systemName instead of the key value, the code will be much more readable.Am I way off base? Does this sound like a solid design? Has anyone run into a problem like this before?Any guidence would be appreciated.Nic |
|
|
Onamuji
Aged Yak Warrior
504 Posts |
Posted - 2003-01-17 : 12:45:55
|
| make one stored procedure that loads parameters for the values ...CREATE TABLE Status ( StatusID INT NOT NULL PRIMARY KEY NONCLUSTERED Name VARCHAR(64) NOT NULL UNIQUE CLUSTERED)INSERT INTO Status (StatusID, Name) VALUES (1, 'Complete')INSERT INTO Status (StatusID, Name) VALUES (1, 'Incomplete')INSERT INTO Status (StatusID, Name) VALUES (1, 'Finaled')GOCREATE PROCEDURE GetStatusID @Complete INT = NULL OUTPUT, @Incomplete INT = NULL OUTPUT, @Finaled INT = NULL OUTPUTAS SET NOCOUNT ON SET @Complete = 1 SET @Incomplete = 2 SET @Finaled = 3GOCREATE PROCEDURE CompleteStuff AS SET NOCOUNT ON DECLARE @complete INT EXEC GetStatusID @Complete = @complete OUTPUT SELECT * FROM Stuff WHERE StatusID = @completeGO |
 |
|
|
Page47
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2003-01-17 : 12:57:28
|
| Bad Design? Yes ...1.) You haven't defined a primary key.2.) All your columns are nullable.3.) You use hungarian notation for your table and column names.4.) You name you table in the singular, when it is a set (plural).5.) None of the status codes you mention are close to 20 characters.6.) You provide no check contraints to define allowable names.7.) I'm not sure I understand the diff between system and display. If a user wants to change "incomplete" to "not done" is that a new status? Smells like it to me ... The surrogate key vs. natural key debate has been played out, but I believe in the case of "look-up" tables, a natural key is better.Jay White{0} |
 |
|
|
nic
Posting Yak Master
209 Posts |
Posted - 2003-01-17 : 13:45:43
|
| Thanks for the suggestions.Just to clarify, I wrote the CREATE table statement just to help clarify the issue. Adding all the key, null information makes it more difficult to read. I was just asking about general design ideas. Point well taken though.That being said, if I make the key value to be a natural key, the whole issue is resolved. The key would be the systemName and the display would be the displayName (These would not be the actual column names obviously)One last question. In regards to these "look-up" tables, is it a good idea to have a seperate table for each "look-up" type (even if that means having a large number of these tables?)In the application I have many dropdown lists (states,userRoles,applicationTypes,driverCounts,statusCodes etc) Some of the non-crucial lists I have stored in a "General" dropdown table where I can provide the dropdown type and get the list in return. This saves some effort but if it is not a good practice, I can put each list in a seperate table.Would this be better?Nic |
 |
|
|
Page47
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2003-01-17 : 14:08:31
|
quote: One last question. In regards to these "look-up" tables, is it a good idea to have a seperate table for each "look-up" type (even if that means having a large number of these tables?)
Yes ... many look-up tables = good ...Until someone comes up with a FK constraint with predicate logic built, the only way to enfore DRI is with many look-up tables.Jay White{0}Edited by - Page47 on 01/17/2003 14:08:57 |
 |
|
|
robvolk
Most Valuable Yak
15732 Posts |
Posted - 2003-01-17 : 14:43:15
|
Here's some ideas:http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=22326The structure I detailed there may or may not work for you, but it let me set up an unlimited number of categories using only two tables (and it's all natural keys! mostly anyway...codes were used for question groups/categories) It also had a "display name" in the form of QuestionText and AnswerText. The data for this would look like:QuestionsQuestionGroup Question QuestionTextWC Contact Are you the authorized account contact?WC Address What is the authorized address for your account?WC Units How many units did you purchase?AnswersQuestionGroup Question Answer AnswerTextWC Contact Yes Yes, caller is authorized account contactWC Contact No No, caller is just some schmuckWC Address Yes Yes, caller has correct addressWC Address No No, caller doesn't know correct addressWC Units 1 One unitWC Units 2 Two unitsWC Units 3 Three unitsWC Units 4 Four unitsWC Units 5 Five unitsWC Units 6+ Six or more units The actual answers for each caller were stored something like this:CustomerResponsesCustomerName QuestionGroup Question AnswerJoe Smith WC Contact YesJoe Smith WC Address YesJoe Smith WC Units 5 For your dropdown lists, you can change the Questions table and get rid of the QuestionGroup column; each question would be a separate dropdown name or category. The Answers table would also lose the QuestionGroup column, so would CustomerResponses. However, if you had multiple applications, keeping that kind of column as "AppName" or "Category" would allow you to manage every app's controls, questions, and answers from 3 tables. This would be the "predicate logic" Jay was looking for. You could certainly use more tables to save actual responses if you wanted to, but the DRI can still be done using 2 question and answer tables.Just in case someone jumps into this thread about "you're wasting all that space using natural keys" A) get over it, space is cheap, and there's not one superfluous column in any table, B) I can get any query I need from one table and one table only, I only need to join to get the actual descriptions of the question and answer text, which I don't even need on a report because the values are descriptive enough, C) the clustering on this table makes the most frequent queries lightning fast, a join could only slow it down, D) all the columns make up the key and prevent duplicate answers, a surrogate key and an additional unique index would only slow it down (in the real system the customername actually was an account number generated by another system), and lastly E) if you can show me a surrogate key design that does the same thing using less than 5 tables and allows all reporting from one table only, post it here, I'd love to see it. Seriously, I'm not joking.Also, using natural keys here PREVENTS people from making arbitrary requests like "I don't like Yes/No, I want to change it to Authorized/Unauthorized". This is not a limitation, it's a conscious design decision. Jay is right about incomplete and not done, are they really the same or not? If they're the same, why change it? If they're not, then they're two different things and need to be stored as such. If you used a surrogate key you could very easily change every single answer or description into something that means the exact opposite of the original value.Edited by - robvolk on 01/17/2003 14:45:06 |
 |
|
|
Onamuji
Aged Yak Warrior
504 Posts |
Posted - 2003-01-17 : 15:08:10
|
| good point on data integrity ... just wish business integrity was the same ... positions change so damn much ... from Director of IT ... to Cheif Information Office ... to who knows whats next ... when someone hears a different buzz word they think its catchy and command their minions to change every as such ... |
 |
|
|
byrmol
Shed Building SQL Farmer
1591 Posts |
Posted - 2003-01-17 : 19:20:01
|
Jay,Regarding "Look-ups". I recently had the displeasure of working on a large db application that used a "code table". It contained about 50 domains and of course had no integrity with the data tables. At first everything appeared to be fine as the DB was exposed only to 1 front-end. Demand grew for the data and many requests where made to the DB from "outside" of the original front-end. XML, bulk loads etc... The result was as expected... garbage got into the db. Due to the app design, the front-end starts to throw errors due to the data. A good example not to do but it did get me thinking about a "better" code table...--"Code Table"CREATE TABLE Domains (Domain VARCHAR(35) NOT NULL, Value SQL_VARIANT NOT NULL CHECK(SQL_VARIANT_PROPERTY(Value,'TotalBytes') <= 865), IsDefault BIT NOT NULL DEFAULT(0), Constraint PK_Domains Primary Key(Domain, Value))go--Integrity FunctionCREATE FUNCTION fnIsDomain(@Domain VARCHAR(35),@Value SQL_VARIANT)RETURNS BITASBEGINDECLARE @BIT BITSET @BIT = 0IF EXISTS(SELECT 1 FROM Domains WHERE Domain = @Domain and Value = @Value) SET @BIT = 1RETURN @BITENDGO--Default generatorCREATE FUNCTION fnDefault(@Domain VARCHAR(35))RETURNS SQL_VARIANTASBEGINRETURN (SELECT Value from Domains where Domain = @Domain and IsDefault = 1)ENDgo--Domain data (Gender is not a good example but you get the picture)INSERT TABLE Domains (Domain, Value, IsDefault) SELECT 'Gender','M',0 UNION ALL SELECT 'Gender','F',0 UNION ALL SELECT 'Gender','?',1GO--In use...CREATE TABLE Person (PersonID int NOT NULL Primary Key, Gender CHAR(1) NOT NULL CHECK(dbo.IsDomain('Gender',Gender) = 1)DEFAULT(CAST(dbo.fnDefault('Gender') AS CHAR(1))GONote the CHECK constraint in the DOMAINS table for SQL Server's 900 byte index length.Its not perfect but it is a little better...Thoughts, comments...DavidM"SQL-3 is an abomination.." |
 |
|
|
robvolk
Most Valuable Yak
15732 Posts |
Posted - 2003-01-17 : 19:53:44
|
Hmmmm...I'm almost tempted to beat you up on the variant column but I see what you're doing there. How does it handle nvarchar vs. varchar though? Or does it pretty much convert them to nvarchar and compare it that way? I wonder how a long char/varchar string that's near the byte limit would affect it.What about this as an alternate Person table structure:CREATE TABLE Person (PersonID int NOT NULL Primary Key, GenderDomain varchar(35) NOT NULL DEFAULT('Gender') CHECK (GenderDomain='Gender'),Gender char(1) NOT NULL DEFAULT(CAST(dbo.fnDefault('Gender') AS CHAR(1)CONSTRAINT FK_Domain FOREIGN KEY (GenderDomain, Gender) REFERENCES Domains(Domain, Value))Yeah, I know: WTF is with the GenderDomain column? But think in terms of a smaller column maybe 2-3 characters, similar to the QuestionGroup column in the Questions table I posted earlier. I've used similar designs in other databases having multiple tables referencing a table like Questions->QuestionGroup, with a fixed column like GenderDomain. It made each table's references consistent, even though it used some extra space. Basically, if had to have two different CustomerResponses tables, one for group WC and another for group WVC, they would both reference Questions on the same columns.YOUR thoughts, comments...? Feel free to tell me I'm smoking crack, I wouldn't disagree |
 |
|
|
byrmol
Shed Building SQL Farmer
1591 Posts |
Posted - 2003-01-18 : 05:08:28
|
| The variant column is dodgy, but I feel it is as good a use for a variant as it gets...Haven't tested all the data types yet or pushed the 865 byte length for strings..I think both methods achieve the same level of integrity but with significant differences.The Question example uses a Type (QuestionGroup) and then its Sub Type (Question) in the table. That is, QuestionGroup is a domain and so is Question.The Domains example is just one type (Gender) and its values. More a semantic difference but significant. Explaination..When you applied the technique to the Person table (in context to the Domain approach), we seem to have broken 3NF (Each attribute relies on the Key, the whole key and nothing but key...). We could create a view of the Person table that projects away the GenderDomain to give it a 3NF appearance though...The Question example does not break 3NF.The Question example uses built in RI while the Domains is custom made. That seems to make sense...DavidM"SQL-3 is an abomination.." |
 |
|
|
|
|
|
|
|