Securing values for other tables from enumerable table in SQL Server database - sql

English is not my native language, so I might have misused words Enumerator and Enumerable in this context. Please get a feel for what I'm trying to say and correct me if I'm wrong.
I'm looking into not having tables for each enumerator I need in my database.
I "don't want" to add tables for (examples:) service duration type, user type, currency type, etc. and add relations for each of them.
Instead of a table for each of them which values will probably not change a lot, and for which I'd have to create relationships with other tables, I'm looking into having just 2 tables called Enumerator (eg: user type, currency...) and Enumerable (eg: for user type -> manager, ceo, delivery guy... and for currency -> euro, dollar, pound...).
Though here's the kicker. If I implement it like that, I'm loosing the rigidity of the foreign key relationships -> I can't accidentally insert a row in users table that will have a user type of some currency or service duration type, or something else.
Is there another way to resolve the issue of having so many enumerators and enumerables with the benefit of having that rigidity of the foreign key and with the benefit of having all of them in just those 2 tables?
Best I can think of is to create a trigger for BEFORE UPDATE and BEFORE INSERT to check if (for example) the column type of user table is using the id of the enumerable table that belongs to the correct enumerator.
This is a short example in SQL
CREATE TABLE [dbo].[Enumerator]
(
[Id] INT NOT NULL PRIMARY KEY,
[Name] VARCHAR(50)
)
CREATE TABLE [dbo].[Enumerable]
(
[Id] INT NOT NULL PRIMARY KEY,
[EnumeratorId] INT NOT NULL FOREIGN KEY REFERENCES Enumerator(Id),
[Name] VARCHAR(50)
)
INSERT INTO Enumerator (Id, Name)
VALUES (1, 'UserType'),
(2, 'ServiceType');
INSERT INTO Enumerable (Id, EnumeratorId, Name) -- UserType
VALUES (1, 1, 'CEO'),
(2, 1, 'Manager'),
(3, 1, 'DeliveryGuy');
INSERT INTO Enumerable (Id, EnumeratorId, Name) -- ServiceDurationType
VALUES (4, 2, 'Daily'),
(5, 2, 'Weekly'),
(6, 2, 'Monthly');
CREATE TABLE [dbo].[User]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY (1,1),
[Type] INT NOT NULL FOREIGN KEY REFERENCES Enumerable(Id)
)
CREATE TABLE [dbo].[Service]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY (1,1),
[Type] INT NOT NULL FOREIGN KEY REFERENCES Enumerable(Id)
)
The questions are:
Is it viable to resolve enumerators and enumerables with 2 tables and with before update and before insert triggers, or is it more trouble than it's worth?
Is there a better way to resolve this other than using before update and before insert triggers?
Is there a better way to resolve enumerators and enumerables that is not using 2 tables and triggers, nor creating a table with relations for each of them?
I ask for your wisdom as I don't have one or more big projects behind me and I didn't get a chance to create a DB like this until now.

Related

How should I handle multiple foreign keys that all point to the same column in another table?

So let's say I have these two tables...
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='UnitsDef' AND xtype='U')
CREATE TABLE UnitsDef
(
UnitsID INTEGER PRIMARY KEY,
UnitsName NVARCHAR(32) NOT NULL,
UnitsDisplay NVARCHAR(8) NOT NULL
);
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='Dimensions' AND xtype='U')
CREATE TABLE Dimensions
(
DimID INTEGER PRIMARY KEY IDENTITY(0,1),
DimX FLOAT,
DimXUnitsID INTEGER DEFAULT 0,
DimY FLOAT,
DimYUnitsID INTEGER DEFAULT 0,
DimZ FLOAT,
DimZUnitsID INTEGER DEFAULT 0,
FOREIGN KEY (DimXUnitsID) REFERENCES UnitsDef(UnitsID),
FOREIGN KEY (DimYUnitsID) REFERENCES UnitsDef(UnitsID),
FOREIGN KEY (DimZUnitsID) REFERENCES UnitsDef(UnitsID)
);
I'll insert data into the first table similar to this...
INSERT INTO UnitsDef (UnitsID, UnitsName, UnitsDisplay) VALUES (0, 'inch', 'in.');
INSERT INTO UnitsDef (UnitsID, UnitsName, UnitsDisplay) VALUES (1, 'millimeter', 'mm');
INSERT INTO UnitsDef (UnitsID, UnitsName, UnitsDisplay) VALUES (2, 'degree', '°');
Am I going about this the right way? This is a simplified version of the problem, but I need to know which unit each measurement is given in. Is there a better design practice for this type of situation?
How would I handle the ON DELETE and ON UPDATE for these foreign keys? If I try to cascade deletes and updates, SQL Server would not be so happy about that.
Your method is pretty good. I would make the suggestion right off that UnitsId be an identity column, so it gets incremented. Your inserts would then be:
INSERT INTO UnitsDef (UnitsName, UnitsDisplay) VALUES ('inch', 'in.');
INSERT INTO UnitsDef (UnitsName, UnitsDisplay) VALUES ('millimeter', 'mm');
INSERT INTO UnitsDef (UnitsName, UnitsDisplay) VALUES ('degree', '°');
You should also make the string columns unique in UnitsDef and give them case-sensitive collations. After all, Ml and ml are two different things ("M" is mega and "m" is milli).
You might also want to combine the units and values into a single type. This has positives and negatives. For me it adds overhead, but it can help if you want to support a fuller algebra of types.

Design SQL tables

I have a set of business objects, which will need to store properties specific to the object type. I want these properties to be definable by the user at runtime. My solution for that is to have a table for each object type that contains the property keys for that object type.
For example, a user object may have a set of properties: fname, lname, address, phone, SSN. And if desired, that list can be built upon within the application, to now include email.
I then have a set of tables which contain all the possible values for a given property. So I'd have a table containing all addresses, a table containing all emails, etc. These may be used by different object types, and some property keys may have multiple values. If a a new property is created, a new table to contain that new set of values would also be created.
The problem I'm having is mapping out everything, in such a way that I can identify the value of each property, for each instance, of each object.
Any suggestions on what I might be missing, what I should be looking into, or any other solutions to the problem would be much appreciated.
Thanks!
Disclaimer: I'm a relativity new self taught programmer. Forgive any ignorance or shortcoming.
You could perhaps have something like this. I have not tested the code so please excuse any typos. The code is MS-SQL
CREATE TABLE tblUserDefinedEntity
(
UserDefinedEntityID INT NOT NULL PRIMARY KEY,
Name VARCHAR(255)
)
INSERT INTO tblUserDefinedEntity
VALUES
(1,'Person'),
(2,'Business')
-- properties each entity can have.
CREATE TABLE tblUserDefinedEntityProperty
(
UserDefinedEntityPropertyID INT NOT NULL PRIMARY KEY,
Name VARCHAR(255)
)
INSERT INTO tblUserDefinedEntityProperty
VALUES
(1,'FirstName'),
(2,'Surname'),
(3,'Address')
--maps properties to entities EG a person has a firstname
CREATE TABLE tblUserDefinedEntityPropertyMapping
(
ID INT NOT NULL PRIMARY KEY,
UserDefinedEntityID INT FOREIGN KEY REFERENCES tblUserDefinedEntityID(UserDefinedEntityID),
UserDefinedEntityPropertyID INT FOREIGN KEY REFERENCES tblUserDefinedEntityPropertyID(UserDefinedEntityPropertyID ),
)
-- a person has a firstname, surname and address, a business has a firstname and address
INSERT INTO tblUserDefinedEntityPropertyMapping
VALUES
(1,1,1),
(2,1,2),
(3,1,3),
(4,2,1),
(5,2,3)
--all the available values for each attribute, eg all the firstnames, surnames and addresses
CREATE TABLE tblValue
(
ValueID INT NOT NULL PRIMARY KEY,
Value VARCHAR(255),
UserDefinedEntityPropertyID INT FOREIGN KEY REFERENCES tblUserDefinedEntityProperty (UserDefinedEntityPropertyID),
)
INSERT INTO tblValue
VALUES
(1,'John',1),
(2,'James,1),
(3,'Jill',1),
(4,'Smith',2),
(5,'Jones',2),
(6,'123 Fake Street',3)
(7,'124 Fake Street',3)
--creating instances of your entities and assigning them values
CREATE TABLE tblDataEnty
(
DataItemID INT,
UserDefinedEntityPropertyMappingID INT FOREIGN KEY REFERENCES tblUserDefinedEntityPropertyMapping(UserDefinedEntityPropertyMappingID),
ValueID INT FOREIGN KEY REFERENCES tblValue(ValueID),
PRIMARY KEY (DataItemID, UserDefinedEntityPropertyMappingID)
)
-- we have added
-- john jones (no address)
-- james smith (124 fake street)
INSERT INTO tblDataEnty
VALUES
(1,1,1),
(1,2,5),
(2,1,2),
(2,2,4),
(2,3,7)

Foreign key to 1 of several tables

I have two tables (OrderFreshGoods and OrderUtensils) and then I have an AuditTrail table. The AuditTrail table is related to the OrderFreshGoods table but I want to change it so that an Audit must relate to either an OrderFreshGoods or OrderUtensils record. I have seen a lot of solutions where the Audit table say would have 2 foreign keys (OrderIDFresh, OrderIDUtensils and it is optional that 1 of them must be populated). Note that I do not want that solution. I want the Audit table to have 1 foreign key (OrderID) and it must relate to either OrderFreshGoods.OrderID or OrderUtensils.OrderID.
Also my two order tables have no fields in common and are used in a large number of queries around the system so I don't want a parent table for both types of order.
Can anybody help? My sql script is below, the comments should help explain my tests...
--Setup tables
create table OrderFreshGoods (OrderID int not null primary key, sellBy date, name varchar(20))
go
create table OrderUtensils (OrderID int not null primary key, requiresOver18CheckForKnives bit, colour varchar(20), title varchar(20))
go
create table AuditTrail (AuditId int not null primary key, OrderID int, timeOfEvent date, eventDescription varchar(100))
go
--Base data
insert into OrderFreshGoods values (7, DATEADD(dd, 3, getdate()), 'Organic milk')
insert into OrderUtensils values (8, 0, 'Red', 'Garlic crusher')
--Test data!!!!!!!!!!!!!!
--This should work
insert into AuditTrail values (15, 7, getdate(), 'Logging order for Organic Milk from Corkys Coffee shop.')
--This should work
insert into AuditTrail values (16, 8, getdate(), 'Logging order for a Red Garlic Crusher from Perrys Pizza Place.')
--This should not be allowed
insert into AuditTrail values (17, 9, getdate(), 'Wrongly adding an audit entry before the order, please stop me now!')
--This should not be allowed
insert into AuditTrail values (18, null, getdate(), 'Oh dear, bad code has caused the OrderId to be lost, please stop me now!')
What you want is not possible as you describe it, it goes against the very premise of Relational databases.
If you leave out the actual Foreign Key, then you can populate the AuditTrail.OrderId with whatever you want.
But you'd lose the referential integrity check, so your third insert into AuditTrail statement wouldn't fail. That could then be fixed by applying an on-insert trigger which does a reference check. But it would still not prevent Orders from being deleted, causing the pseudo-relation to go bad again.
Another and perhaps much better alternative is to add an AuditId field to both of the Order tables, and fill that as needed.
If you want to use always proper relations, then I can see next solution:
create yet another table xxxx, like:
create table xxxx(id int identity(1,1) primary key)
add foreign keys in all your auditable tables to xxxx(id)
create new records in xxxx while adding records to auditable tables (triggers can be handy) (you may include more info into xxxx)
add foreign key in audit table to xxxx(id)
This way you can insert into audit table only records, pointing to xxxx - and these are always related to actual orders (and do not vanish when some order gets deleted either).

Database Normalization using Foreign Key

I have a sample table like below where Course Completion Status of a Student is being stored:
Create Table StudentCourseCompletionStatus
(
CourseCompletionID int primary key identity(1,1),
StudentID int not null,
AlgorithmCourseStatus nvarchar(30),
DatabaseCourseStatus nvarchar(30),
NetworkingCourseStatus nvarchar(30),
MathematicsCourseStatus nvarchar(30),
ProgrammingCourseStatus nvarchar(30)
)
Insert into StudentCourseCompletionStatus Values (1, 'In Progress', 'In Progress', 'Not Started', 'Completed', 'Completed')
Insert into StudentCourseCompletionStatus Values (2, 'Not Started', 'In Progress', 'Not Started', 'Not Applicable', 'Completed')
Now as part of normalizing the schema I have created two other tables - CourseStatusType and Status for storing the Course Status names and Status.
Create Table CourseStatusType
(
CourseStatusTypeID int primary key identity(1,1),
CourseStatusType nvarchar(100) not null
)
Insert into CourseStatusType Values ('AlgorithmCourseStatus')
Insert into CourseStatusType Values ('DatabaseCourseStatus')
Insert into CourseStatusType Values ('NetworkingCourseStatus')
Insert into CourseStatusType Values ('MathematicsCourseStatus')
Insert into CourseStatusType Values ('ProgrammingCourseStatus')
Insert into CourseStatusType Values ('OperatingSystemsCourseStatus')
Insert into CourseStatusType Values ('CompilerCourseStatus')
Create Table Status
(
StatusID int primary key identity(1,1),
StatusName nvarchar (100) not null
)
Insert into Status Values ('Completed')
Insert into Status Values ('Not Started')
Insert into Status Values ('In Progress')
Insert into Status Values ('Not Applicable')
The modified table is as below:
Create Table StudentCourseCompletionStatus1
(
CourseCompletionID int primary key identity(1,1),
StudentID int not null,
CourseStatusTypeID int not null CONSTRAINT [FK_StudentCourseCompletionStatus1_CourseStatusType] FOREIGN KEY (CourseStatusTypeID) REFERENCES dbo.CourseStatusType (CourseStatusTypeID),
StatusID int not null CONSTRAINT [FK_StudentCourseCompletionStatus1_Status] FOREIGN KEY (StatusID) REFERENCES Status (StatusID),
)
I have few question on this:
Is this the correct way to normalize it ? The old table was very helpful to get data easily - I can store a student's course status in a single row, but now 5 rows are required. Is there a better way to do it?
Moving the data from the old table to this new table seems to be not an easy task. Can I achieve this using a query or I have to manually to do this ?
Any help is appreciated.
vou could also consider storing results in flat table like this:
studentID,courseID,status
1,1,"completed"
1,2,"not started"
2,1,"not started"
2,3,"in progress"
you will also need additional Courses table like this
courserId,courseName
1, math
2, programming
3, networking
and a students table
students
1 "john smith"
2 "perry clam"
3 "john deere"
etc..you could also optionally create a status table to store the distinct statusstrings statusstings and refer to their PK instead ofthestrings
studentID,courseID,status
1,1,1
1,2,2
2,1,2
2,3,3
... etc
and status table
id,status
1,"completed"
2,"not started"
3,"in progress"
the beauty of this representation is: it is quite easy to filter and aggregate data , i.e it is easy to query which subjects a particular person have completed, how many subjects are completed by an average student, etc. this things are much more difficult in the columnar design like you had. you can also easily add new subjects without the need to adapt your tables or even queries they,will just work.
you can also always usin SQLs PIVOT query to get it to a familiar columnar presentation like
name,mathstatus,programmingstatus,networkingstatus,etc..
but now 5 rows are required
No, it's still just one row. That row simply contains identifiers for values stored in other tables.
There are pros and cons to this. One of the main reasons to normalize in this way is to protect the integrity of the data. If a column is just a string then anything can be stored there. But if there's a foreign key relationship to a table containing a finite set of values then only one of those options can be stored there. Additionally, if you ever want to change the text of an option or add/remove options, you do it in a centralized place.
Moving the data from the old table to this new table seems to be not an easy task.
No problem at all. Create your new numeric columns on the data table and populate them with the identifiers of the lookup table records associated with each data table record. If they're nullable, you can make them foreign keys right away. If they're not nullable then you need to populate them before you can make them foreign keys. Once you've verified that the data is correct, remove the old de-normalized columns. Done.
In StudentCourseCompletionStatus1 you still need 2 associations to Status and CourseStatusType. So I think you should consider following variant of normalization:
It means, that your StudentCourseCompletionStatus would hold only one CourseStatusID and another table CourseStatus would hold the associations to CourseType and Status.
To move your data you can surely use a query.

How to Paste desired selection in SQL Server 2008

So I have a problem, I was wondering is there a way of inserting the (ID VALUES) to each column since I forgot. I have a really long code. Is there a way for me to select a piece of code to paste the number ID values or a Method of some sort? Or is my only option to do it manually?
CREATE TABLE tASTON
(AsM_ID int Primary Key Not Null IDENTITY(1,1),Make Nvarchar(50), Model nvarchar (50))
Insert into tASTON(AsM_ID,Make,Model) Values ('Aston Martin','DB4')
Insert into tASTON (AsM_ID,Make,Model) Values ('Aston Martin','DB5')
Insert into tASTON (AsM_ID,Make,Model) Values ('Aston Martin','DB6')
Insert into tASTON (AsM_ID,Make,Model) Values ('Aston Martin','DB7')
Insert into tASTON (AsM_ID,Make,Model) Values ('Aston Martin','Vanquish') etc...
Seeing it's an IDENTITY column, there's no need to manually INSERT values. This ought to work:
Insert into tASTON(Make,Model) Values ('Aston Martin','DB4')
Update: Removed brand, as it was confusing and a mistake on my part. Replaced Brand with Make, instead.*
Unrelated, but judging by the name of the table, are you really creating one table per brand of car? If so, I'd suggest creating a make and a car table, where you then implement a key relationship between tCar and tMake:
CREATE TABLE tCAR
(
CAR_ID int Primary Key Not Null IDENTITY(1,1)
, MAKE_ID int FOREIGN KEY REFERENCES tMake(MAKE_ID)
, Model nvarchar (50)
)