When is a SQL Server foreign key table too much? - sql

When I have the below two tables, would the StatusTypes table be considered as overkill? i.e. is there more benefit to using it than not?
In this situation I don't expect to have to load these statuses up in an admin backend in order to add or change/ delete them, but on the other hand I don't often like not using foreign keys.
I'm looking for reasons for and against separating out the status type or keeping it in the Audit table.
Any help would be appreciated.
-- i.e. NEW, SUBMITTED, UPDATED
CREATE TABLE [dbo].[StatusTypes](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](250) NOT NULL,
CONSTRAINT [PK_StatusTypes] PRIMARY KEY CLUSTERED ([ID] ASC)
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Audits](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Description] [nvarchar](500) NULL,
[Country_Fkey] [int] NOT NULL,
[User_Fkey] [int] NOT NULL,
[CreatedDate] [date] NOT NULL,
[LastAmendedDate] [date] NULL,
[Status_Fkey] [int] NOT NULL,
CONSTRAINT [PK_Audits] PRIMARY KEY CLUSTERED ([ID] ASC)
) ON [PRIMARY]
GO

In this situation I like to keep the lookup table to enforce the status being one of a set of types. Some databases have an enum type, or can use check constraints, but this is the most portable method IMO.
However, I make the lookup table containing only a single string column containing the type's name. That way you don't have to actually join to the lookup table and your ORM (assuming you use one) can be completely unaware of it.
In this case the schema would look like:
CREATE TABLE [dbo].[StatusTypes](
[ID] [nvarchar](250) NOT NULL,
CONSTRAINT [PK_StatusTypes] PRIMARY KEY CLUSTERED ([ID] ASC)
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Audits](
[ID] [int] IDENTITY(1,1) NOT NULL,
...
[Status] [nvarchar](250) NOT NULL,
CONSTRAINT [PK_Audits] PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_Audit_Status] FOREIGN KEY (Status) REFERENCES StatusTypes(ID)
) ON [PRIMARY]
GO
And a query for audit items of a particular type would be:
SELECT ...
FROM Audits
WHERE Status = 'ACTIVE'
So referential integrity is still enforced but queries don't need an extra join.

I'll offer a counter-argument: Use your development time where it is most useful. Maybe you don't need this runtime-check that much. Maybe you can use your development time for some other check that is more useful.
Is it even likely that an invalid status value will be set? You application surely uses a set of constants or an enum so it is unlikely that some rogue value slips in.
That said, there is a lot of value in ensuring integrity. I like to cover all my "enum" columns with a BETWEEN check constraint which is quickly done and even faster at runtime.

Related

Update PK columns in table having clustered keys

I have a database with tables that have clustered primary keys. I believe the term I picked up on is use of a Natural Key. The frontend to the SQL database is programmed to affect changes to all related foreign key tables for the 3,000 selected values which I want to modify. It takes about 13 seconds per change. I have a need to do this in a much shorter timeframe if possible.
The reason for doing this is prep work to migrate to a new CMMS program.
I found reference for use of ON UPDATE CASCADE, but I am not certain this applies.
Among many references, I used the following:
https://social.technet.microsoft.com/Forums/sqlserver/it-IT/23294919-3e6a-4146-a70d-66fa155ed1b3/update-primary-key-column-in-sql-server?forum=transactsql
An example of 2 of the 15 tables having the same named [EQNUM] column follows. Table A is the table that is first modified using the frontend. I left out many columns for each table:
CREATE TABLE A
(
[EQNUM] [varchar](30) NOT NULL,
CONSTRAINT [PK_A] PRIMARY KEY CLUSTERED ([EQNUM] ASC)
)
GO
SET ANSI_PADDING OFF
GO
CREATE TABLE B
(
[EQNUM] [varchar](30) NOT NULL,
[ColA] [varchar](10) NOT NULL,[ColB] [datetime] NOT NULL,
[ColC] [varchar](30) NOT NULL, [ColD] [varchar](30) NOT NULL,
[ColE] [varchar](30) NOT NULL, [ColF] [varchar](30) NOT NULL,
[ColG] [varchar](11) NOT NULL,[ColH] [varchar](10) NOT NULL,
[ColI] [datetime] NOT NULL,[ColJ] [varchar](15) NOT NULL,
[ColK] [int] NULL,
CONSTRAINT [PK_B]
PRIMARY KEY CLUSTERED ([EQNUM] ASC,
[ColA] ASC,[ColB] ASC,[ColC] ASC,[ColD] ASC,[ColE] ASC,
[ColF] ASC,[ColG] ASC,[ColH] ASC,[ColI] ASC,[ColJ] ASC)
)
An example of 1 of 4 sets of UPDATE queries, for which I believe the added first and last ALTER TABLE lines would allow me to affect the update:
ALTER TABLE A NOCHECK CONSTRAINT [PK_EQUIP]
UPDATE A
SET [EQNUM] = REPLACE([EQNUM],'-B','-B0')
WHERE [EQNUM] LIKE '%-A[1-9][0-5][0-9]-%' OR
[EQNUM] LIKE '%-A[1-9][A-F][0-5][0-9]-%' OR
ALTER TABLE A CHECK CONSTRAINT [PK_EQUIP]
ALTER TABLE A NOCHECK CONSTRAINT [PK_B]
UPDATE B
SET [EQNUM] = REPLACE([EQNUM],'-B','-B0')
WHERE [EQNUM] LIKE '%-A[1-9][0-5][0-9]-%' OR
[EQNUM] LIKE '%-A[1-9][A-F][0-5][0-9]-%' OR
ALTER TABLE A CHECK CONSTRAINT [PK_B]
Is it this simple, or am I missing something? Is there a better way?

Cannot define PRIMARY KEY Constraint on nullable column when column is already NOT NULL

I have a table called Cases that has information about interviews such as IV_Date, IV_Length, Interviewer, etc. I also have a field within Cases called Case_Code (a varchar) which is a six-character (three letters + three numbers) identifier; e.g. "ABC123" or "ZZZ999." There is a foreign key on ContactID (an int), which points to a Contact table. I have created a computed column which is the PRIMARY KEY of this Cases table, called CaseID. CaseID is simply a concatenation of Case_Code and ContactID. So, ContactID "25" working on case "ZZZ999" is given a CaseID of "ZZZ99925". Neither Case_Code nor ContactID accepts nulls, so CaseID obviously does not. When setting up CaseID I created it as NOT NULL, PRIMARY KEY, and used the formula "[Case_Code] + CONVERT([varchar], [contactID], 0)". I thought everything was working fine but when I try to rearrange any fields in the SQL Studio table design view, I get thrown this error:
'Cases (dbo)' table
- Unable to create index 'PK_Cases_1'.
Cannot define PRIMARY KEY constraint on nullable column in table 'Cases'.
Could not create constraint. See previous errors.'
I do not understand why I'm getting this error, since all NOT NULL columns in my table contain data. Any help or ideas would be greatly appreciated. Thanks!
Edit with code:
CREATE TABLE [dbo].[Cases](
[ContactID] [int] NOT NULL,
[Case_Code] [varchar](16) NOT NULL,
[Assigned_To] [varchar](100) NULL,
[LEK_Interviewer] [varchar](255) NULL,
[Case_Notes] [varchar](max) NULL,
[IV_Status] [varchar](100) NULL,
[IV_Quality] [numeric](18, 0) NULL,
[IV_Date] [date] NULL,
[IV_Length] [varchar](50) NULL,
[Address] [varchar](100) NULL,
[City] [varchar](100) NULL,
[State] [varchar](50) NULL,
[Zip] [varchar](25) NULL,
[Country] [varchar](50) NULL,
[Total_Honorarium] [money] NULL,
[Currency] [varchar](20) NULL,
[Last_Update] [varchar](50) NULL,
[CaseID] AS ([Case_Code]+CONVERT([varchar],[contactID],0)) PERSISTED NOT NULL,
CONSTRAINT [PK_Cases_1] PRIMARY KEY CLUSTERED
(
[CaseID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
A primary key cannot be applied on a column with a null constraint (nullable). When a table is created, by default every column is nullable.
So first off all we have to remove the null constraint and apply a NOT NULL constraint on the column. To do this execute the following query
ALTER TABLE tbl_name alter column column_name int NOT NULL
After that you can apply a primary key on that same column. To do this execute the following query
ALTER TABLE tbl_name ADD PRIMARY KEY (column_name)
I hope this will help you
I bumped into this, too. You can create a PK on a calculated field, as long as it's marked NOT NULL, but the SQL Server table design interface does not handle this situation; it seems to mark all calculated fields as NULLable when modifying a table.
First, I strongly recommend that you always create and modify your tables with scripts, not the GUI. Doing so gives you a record of what you have and what changes you've made; you can run them to recreate your database as of any point in time. As HLGEM mentioned, you can also keep these scripts in source control. Having these scripts makes it easier to do things which are difficult to do with the GUI, and as you've discovered here, they make it possible to do things which are impossible in the GUI.
Within the table designer, delete your PK. You should then be able to save the table, effecting the reordering of fields you wanted to achieve. Next, you need to drop and recreate the calculated field, and finally you can recreate your PK. E.g.:
ALTER TABLE Cases DROP COLUMN CaseID
ALTER TABLE Cases ADD CaseID AS (Case_Code + CAST(ContactID AS CHAR(6))) PERSISTED NOT NULL
ALTER TABLE Cases ADD CONSTRAINT CPK_Cases PRIMARY KEY CLUSTERED (CaseID)
If you have any indices which reference the calculated field (other than the PK, which we've already dropped), you'll need to delete them before you can drop the calculated field. Be sure to re-create them after, of course.
Finally, while Dean did not support his assertion that calculated fields are bad for PKs, he is correct that it would be simpler to put your PK on { Case_Code, ContactID }. You could then stop persisting CaseID (if you even need it all) and save yourself a little disk space.
Use this query
Alter table tablename
add column datatype identity(1,1)
Don't use computed column as a primary key, very bad idea. In your case, just define the PK on both CaseCode and ContactID columns. In the designer, select both columns, right-click and choose "set prinary key" from the menu.
This appears to be a bug in the SQL Server Management Studio. The easiest approach is to make your changes using the designer. Then generate the change script using:
Right-click > Generate Change Script...
Copy the generated SQL or save it to a file. Then, add "NOT NULL" to the computed column in the CREATE TABLE statement and run the SQL file

Composite Keys Constraints

I am creating a database for handbag shopping website in which i have a table called dbo.ProductMatching
CREATE TABLE [dbo].[ProductMatching](
[ProductMatchingID] [int] NOT NULL IDENTITY, -- This can be Made Primary Key but i want to use composite keys
[MainProductID] [int] NOT NULL,
[MainProductColourID] [int] NOT NULL,
[ReferenceProductID] [int] NULL,
[ReferenceProductColourID] [int] NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[UpdatedOn] [datetime] NOT NULL
) ON [PRIMARY]
What i want to do is Make (MainProductID,MainProductColourID) unique and for each this combination i want to make combination of (ReferenceProductID,ReferenceProductColourID) unique also.
e.g suppose i have combination of (MainProductID,MainProdcutColourID) = (1,1) it referes to (ReferenceProductID,ReferenceProductColourID) = (2,2) Then (1,1) can not refer to another (2,3) combination.. i cant make the whole four keys composite keys because it would allow reference (1,1) to (2,3) combination..
i know i can just make exists statement when inserting data or make a before insert trigger or update trigger for data consistency but what i want to know if this can be done with using composite keys.. if not what are other available options...
If I understand the problem then two simple unique constraints
MainProductID,MainProductColourID
and
MainProductID,ReferenceProductID
And I would use
MainProductID,MainProductColourID as a composite PK
(that will satisfy that unique constraint)
If that is wrong then please show more examples that are correct
And more that are incorrect with a reason

Complex foreign key

I have a table similar to this:
CREATE TABLE [dbo].[Table1](
[Option_PK] [bigint] IDENTITY(1,1) NOT NULL,
[Option_1] [varchar](10) NULL,
[Option_2] [varchar](10) NULL,
[Option_3] [varchar](10) NULL)
What I am attempting to do, is add a table driven constraint which can effectively restrict valid entries on a per-column basis. For example, if I made a second table:
CREATE TABLE [dbo].[Table2](
[FK_Name] [varchar](10) NOT NULL,
[FK_Value] [varchar](10) NOT NULL)
I would then want to check that the value stored in Table1, column "Option_1", existed in Table2, column "FK_Value", where the value of "FK_Name" was "Option_1".
Is this possible with either a check or an FK?
** Edit to make the column datatypes match; I hand typed the example table declarations and typo'd, this wasn't relevant to the problem. I know how to do a FK, I don't know how to do an FK like what I'm describing.
Could you not just have 3 tables and three FK?
A FK needs to match types.
CREATE TABLE [dbo].[Option1]([FK_Value] [nchar](10) NOT NULL)
CONSTRAINT [PK_Option1] PRIMARY KEY CLUSTERED ([FK_Value] ASC)
ALTER TABLE [dbo].[Table1] WITH CHECK ADD CONSTRAINT [FK_Table1_Option1] FOREIGN KEY([Option_1])
REFERENCES [dbo].[Table2] ([FK_Value])
GO
Or you could have a column Option1 that defaults to a value of option1
I tried hard coding in a value for Option1 but would not go.
ALTER TABLE [dbo].[FKtest1] WITH CHECK ADD CONSTRAINT [FK_FKtest1_FKtest1] FOREIGN KEY([Option1],[ValueFK])
REFERENCES [dbo].[FKtest1FK] ([PKoption],[PKvalue])
GO
CREATE TABLE [dbo].[Table1](
[Option_PK] [bigint] IDENTITY(1,1) NOT NULL,
[Option_1] [varchar](10) NULL,
[Option_1_FK] [varchar](8) NOT NULL DEFAULT 'OPTION_1',
[Option_2] [varchar](10) NULL,
[Option_2_FK] [varchar](8) NOT NULL DEFAULT 'OPTION_2',
[Option_3] [varchar](10) NULL,
[Option_3_FK] [varchar](8) NOT NULL DEFAULT 'OPTION_3'
)
CREATE TABLE [dbo].[Options](
[FK_Name] [nchar](8) NOT NULL,
[FK_Value] [nchar](10) NOT NULL,
CONSTRAINT [PK_Option1] PRIMARY KEY CLUSTERED ([FK_Name], [FK_Value] ASC)
)
ALTER TABLE [dbo].[Table1] WITH CHECK ADD
CONSTRAINT [FK_Table1_Option1] FOREIGN KEY([Option_1], [Option_1_FK) REFERENCES [dbo].[Options] ([[FK_Value], [FK_Name])
CONSTRAINT [FK_Table1_Option2] FOREIGN KEY([Option_2], [Option_2_FK) REFERENCES [dbo].[Options] ([[FK_Value], [FK_Name])
CONSTRAINT [FK_Table1_Option3] FOREIGN KEY([Option_3], [Option_3_FK) REFERENCES [dbo].[Options] ([[FK_Value], [FK_Name])
GO
Which is untested, unnormalized and ugly. You should probably add constraints to ensure the value of Option_X_FK does not change. Actually, this being T-SQL, you might be able to use computed columns for that, but I'm not sure if including them in a foreign key is allowed.

Indexing for uniqueidentifier column in table

i have created a table where i used unique identifier(GUID) as a primary key of table. Now i need to create a indexing on my table which one will be best for me..i am going to use this table for error logging.
Following is my table structure
CREATE TABLE [dbo].[errors](
[error_id] [uniqueidentifier] NOT NULL,
[assembly_name] [varchar](50) NULL,
[method_name] [varchar](50) NULL,
[person_id] [int] NULL,
[timestamp] [datetime] NULL,
[description] [varchar](max) NULL,
[parameter_list] [varchar](max) NULL,
[exception_text] [nvarchar](max) NULL)
So which table i use as a primary key and index.
Thanks in advance.
You can use that as PK but not good if you use it as clustered index.In that case the GUID will be copied in all the nc index keys and thus makes them much wider and could cause performance issue.Also, this might cause page spilts which is no good.Wide indexes means more space will be used.If you have used GUID to avoid the last page contention issues try to use some sort of hashing technique to make sure that data goes on diff pages.But in that case you have to use same hashing while selecting form table using PK.