How should a IsPrimaryPhone field be modeled in the database? - sql-server

A contact can have one or more phone numbers. A way is needed to set one of them as the "Primary" phone number.
What is the best way to model that?
The way I usually see it done is to simply add a field called "is_primary" to the phone. Yet for this to work, when that field is set to true for one record, then all other related phone numbers needs to be set to false.
Is there a way to model it through a relation so only one bit of data needs to be updated?
It seems this could be done through a linking table called PrimaryPhone with
ContactId and PhoneId. If the link exists for a ContactId/PhoneId, then it would be used in the UI to show that it was the primary. If the primary changed, it would only be a matter of changing the one record.
Or is some other way this should be done?

Assuming you have two tables (Contacts and PhoneNumbers) it is pretty simple. You have a column in Contacts for PrimaryPhoneNumberID and it contains the ID of the PhoneNumber. This ensures you can never have more than 1 marked as the primary at any one point in time.

I might try mine like:
CREATE TABLE Contact
(ContactID int NOT NULL PRIMARY KEY
,CreateDate datetime
)
GO
CREATE TABLE Phone
(PhoneID int identity (1,1) PRIMARY KEY
,ContactID int not null
,[Key] varchar(20) not null
,[Value] varchar(20) not null
,[Status] varchar(20) not null
)
GO
ALTER TABLE Phone
ADD CONSTRAINT FK_cnst FOREIGN KEY (ContactID)
REFERENCES Contact (ContactID)
GO
ALTER TABLE phone ADD CONSTRAINT UX_Constraint UNIQUE (ContactID, [Status])
GO
INSERT contact SELECT 1, GETDATE()
INSERT INTO phone (ContactID, [Key], Value, Status) VALUES (1, 'Office', '455-1212', 'Alternate')
INSERT INTO phone (ContactID, [Key], Value, Status) VALUES (1, 'Home', '555-1212', 'Alternate2')
INSERT INTO phone (ContactID, [Key], Value, Status) VALUES (1, 'Cell', '555-1000', 'Primary')
INSERT INTO phone (ContactID, [Key], Value, Status) VALUES (1, 'Cell', '555-1001', 'MistressHotline')
GO
SELECT *
FROM Phone
GO
DROP TABLE phone, Contact
And then of course go query it to see if it will work out or not.
Thanks

Related

SQL Server how to populate data to table2 when table1 updated

I'm creating a table to store cars, and another table to store the time when the new car was added to the database, can someone please explain to me how to create the relationship to update time automatically when the car was created.
Create table Cars
(
CarID int Primary Key identity(1,1),
Make varchar(50),
Model varchar(50),
Colour varchar(59)
)
create Table TimeLogs
(
AddedOn SYSDATETIME(),
CarId int unique foreign key references Cars(CarId)
)
I would solve this by not using a second table for what should be a column in the Cars table. The table would be designed more appropriately like this.
Create table Cars
(
CarID int Primary Key identity(1,1),
Make varchar(50),
Model varchar(50),
Colour varchar(59),
AddedOn datetime default SYSDATETIME()
)
To automatically update one table whenever another table is updated, you need to use a TRIGGER.
You needs to use insert trigger for the same, as below
CREATE TRIGGER yourNewTrigger ON yourSourcetable
FOR INSERT
AS
INSERT INTO yourDestinationTable
(col1, col2 , col3, user_id, user_name)
SELECT
'a' , default , null, user_id, user_name
FROM inserted
go

How to set "auto insert" foreign key value by using SQL Server?

I have created two tables and also created a relationship between them.
Table students:
create table students
(
[StudentId] NVARCHAR(50) NOT NULL PRIMARY KEY,
[Name] NVARCHAR(50) NOT NULL
);
Table studentprofile:
create table studentprofile
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY(1, 1),
[StudentId] NVARCHAR(50) NOT NULL,
[Address] NVARCHAR(50) NOT NULL,
);
and relationship:
alter table studentprofile
add constraint students_studentprofile_FK
foreign key (StudentId)
references students(StudentId)
on delete cascade on update cascade
But, when I wrote this line:
insert into students values('110111', 'Marik')
the value of StudentId (in table studentprofile) wasn't updated automatically. Why?
Can you tell me how to set the value of StudentId (in table studentprofile) can be inserted automatically whenever I insert into table students?
There is no such thing as insert cascade.
You can implement such a thing by using a trigger for insert on your students table, inserting default values (or nulls) into the studentprofile table:
CREATE TRIGGER students_insert ON students AFTER INSERT
AS
INSERT INTO studentprofile(StudentId, Address)
SELECT StudentId, 'NO ADDRESS'
FROM inserted
Note that your Address column is defined as not null and has no default value, this is why I've used the hard coded 'NO ADDRESS' for it.
However, I agree with the comments on your question: you would be better off inserting the data to the student profile using a different insert statement (perhaps inside a transaction with the insert to students).

Can a SQL Server 2000 table have no PK, and therefore contain duplicate records?

I have an audit table and instead of defining an identity or ticketed column, I'm considering just pushing in the records of the recorded table (via triggers).
Can a SQL Server 2000 table have no PK, and therefore contain duplicate records?
If yes, does all I have to do consist of CREATING the TABLE without defining any constraint on it?
Yes, this is possible, but not necessarily a good idea. Replication and efficient indexing will be quite difficult without a primary key.
Yes a table without a primary key or Unique Constraint can have rows that are duplicated
for example
CREATE TABLE bla(ID INT)
INSERT bla (ID) VALUES(1)
INSERT bla (ID) VALUES(1)
INSERT bla (ID) VALUES(1)
SELECT * FROM bla
GO
Yes a SQL Server 2000 table can have no primary key and contain duplicate records and yes you can simply Create a table without defining any constraint on it. However I would not suggest this.
Instead, since you are creating an audit table for another table. Lets say for this example you have a Person Table and a Person Audit table that tracks changes in the person Table.
Create your Audit Table like this
CREATE TABLE dbo.PersonAuditID
(
PersonAuditID int NOT NULL IDENTITY (1, 1),
PersonId int NOT NULL,
FirstName nvarchar(50) NOT NULL,
LastName nvarchar(50) NOT NULL,
PersonWhoMadeTheChange nvarchar(100) NOT NULL,
TimeOfChange datetime NOT NULL,
ChangeAction int NOT NULL,
/* any other fields here*/
CONSTRAINT [PK_PersonAudit] PRIMARY KEY NONCLUSTERED
(
[PersonAuditID] ASC
)
) ON [PRIMARY]
This will give you a primary key, and keep records unique to the table. It also provides the ability to track who made the change, when the change was made, and if the change was an insert, update or delete.
Your triggers would look like the following
CREATE TRIGGER Insert_PERSON
ON PERSON
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO PERSONAUDIT
(PersonID,
FirstName,
LastName,
PersonWhoMadeTheChange,
TimeOfChange,
ChangeAction,
... other fields here
SELECT
PersonID,
FirstName,
LastName,
User(),
getDate(),
1,
... other fields here
FROM INSERTED
END
CREATE TRIGGER Update_PERSON
ON PERSON
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO PERSONAUDIT
(PersonID,
FirstName,
LastName,
PersonWhoMadeTheChange,
TimeOfChange,
ChangeAction,
... other fields here
SELECT
PersonID,
FirstName,
LastName,
User(),
getDate(),
2,
... other fields here
FROM INSERTED
END
CREATE TRIGGER Delete_PERSON
ON PERSON
AFTER DELETE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO PERSONAUDIT
(PersonID,
FirstName,
LastName,
PersonWhoMadeTheChange,
TimeOfChange,
ChangeAction,
... other fields here
SELECT
PersonID,
FirstName,
LastName,
User(),
getDate(),
3,
... other fields here
FROM DELETED
END
SQL Server 2000+, can have tables without PK. And yes, you create them by no using a constraint.
For an audit table, you need to think of what you may be using the audit data for. And even if you are not doing auditing to spefically use to restore records when unfortunate changes were made, they are inevitably used for this. Will it be easier to identify the record you want to restore if you have a surrogate key that prevents you from accidentally restoring 30 other entries when you only want the most recent? Will a key value help you identify the 32,578 records that were deleted in one batch that needs to be restored?
What we do for auditing is have two tables for each table, one stores information about the batch of records changed, including an auto-incrementing id, the user, the application, the datetime, the number of affected records. The child table then used the ID as the fk and stored the details about the old and new values for each record inserted/updated/deleted. This really helps us when a process bug causes many records to be changed by accident.

Constraint Problem

I created the following table
create table publisher(name varchar(20), city varchar(20));
I want to put following constraint, 'name is having unique with city taken only from 'mumbai', 'pune', 'nasik', 'panji''. I know about unique constraints with name. I searched many about constraint with city, but i don't get it till now. Will you please help me about constraint. I used 'BETWEEN' constraint, but it failed. What should I do?
You can use IN instead of BETWEEN
CREATE TABLE publisher
(
name VARCHAR( 20 ) UNIQUE
, city VARCHAR( 20 ) CHECK ( city IN ('a', 'b') )
);
INSERT INTO publisher
VALUES ('hi','a'); -- Succeeds
INSERT INTO publisher
VALUES ('hi','b'); -- Fails, already have 'hi'
INSERT INTO publisher
VALUES ('hj','c'); -- Fails, 'c' is not a city
I think you're asking for names to be unique within their city, and for the cities to come from a fixed list of possibilities?
If so, the following would work:
create table publisher(
name varchar(20),
city varchar(20),
constraint UQ_NamesWithinCities UNIQUE (name,city),
constraint CK_CityNames CHECK (city in ('mumbai', 'pune', 'nasik', 'panji'))
);

How do I create a multiple-table check constraint?

Please imagine this small database...
Diagram
removed dead ImageShack link - volunteer database diagram
Tables
Volunteer Event Shift EventVolunteer
========= ===== ===== ==============
Id Id Id EventId
Name Name EventId VolunteerId
Email Location VolunteerId
Phone Day Description
Comment Description Start
End
Associations
Volunteers may sign up for multiple events.
Events may be staffed by multiple volunteers.
An event may have multiple shifts.
A shift belongs to only a single event.
A shift may be staffed by only a single volunteer.
A volunteer may staff multiple shifts.
Check Constraints
Can I create a check constraint to
enforce that no shift is staffed by
a volunteer that's not signed up for
that shift's event?
Can I create a check constraint to
enforce that two overlapping shifts
are never staffed by the same
volunteer?
The best place to enforce data integrity is the database. Rest assured that some developer, intentionally or not, will find a way to sneak inconsistent stuff into the database if you let them!
Here's an example with check constraints:
CREATE FUNCTION dbo.SignupMismatches()
RETURNS int
AS BEGIN RETURN (
SELECT count(*)
FROM Shift s
LEFT JOIN EventVolunteer ev
ON ev.EventId = s.EventId
AND ev.VolunteerId = s.VolunteerId
WHERE ev.Id is null
) END
go
ALTER TABLE Shift ADD CONSTRAINT chkSignup CHECK (dbo.SignupMismatches() = 0);
go
CREATE FUNCTION dbo.OverlapMismatches()
RETURNS int
AS BEGIN RETURN (
SELECT count(*)
FROM Shift a
JOIN Shift b
ON a.id <> b.id
AND a.Start < b.[End]
AND a.[End] > b.Start
AND a.VolunteerId = b.VolunteerId
) END
go
ALTER TABLE Shift ADD CONSTRAINT chkOverlap CHECK (dbo.OverlapMismatches() = 0);
Here's some tests for the new data integrity checks:
insert into Volunteer (name) values ('Dubya')
insert into Event (name) values ('Build Wall Around Texas')
-- Dubya tries to build a wall, but Fails because he's not signed up
insert into Shift (VolunteerID, EventID, Description, Start, [End])
values (1, 1, 'Dunbya Builds Wall', '2010-01-01', '2010-01-02')
-- Properly signed up? Good
insert into EventVolunteer (VolunteerID, EventID)
values (1, 1)
insert into Shift (VolunteerID, EventID, Description, Start, [End])
values (1, 1, 'Dunbya Builds Wall', '2010-01-01', '2010-01-03')
-- Fails, you can't start the 2nd wall before you finished the 1st
insert into Shift (VolunteerID, EventID, Description, Start, [End])
values (1, 1, 'Dunbya Builds Second Wall', '2010-01-02', '2010-01-03')
Here are the table definitions:
set nocount on
if OBJECT_ID('Shift') is not null
drop table Shift
if OBJECT_ID('EventVolunteer') is not null
drop table EventVolunteer
if OBJECT_ID('Volunteer') is not null
drop table Volunteer
if OBJECT_ID('Event') is not null
drop table Event
if OBJECT_ID('SignupMismatches') is not null
drop function SignupMismatches
if OBJECT_ID('OverlapMismatches') is not null
drop function OverlapMismatches
create table Volunteer (
id int identity primary key
, name varchar(50)
)
create table Event (
Id int identity primary key
, name varchar(50)
)
create table Shift (
Id int identity primary key
, VolunteerId int foreign key references Volunteer(id)
, EventId int foreign key references Event(id)
, Description varchar(250)
, Start datetime
, [End] datetime
)
create table EventVolunteer (
Id int identity primary key
, VolunteerId int foreign key references Volunteer(id)
, EventId int foreign key references Event(id)
, Location varchar(250)
, [Day] datetime
, Description varchar(250)
)
Question 1 is easy. Just have your Shift table refer directly to EventVolunteer table and you are all set
What I would do is have an Identity column on the EventVolunteer table that auto-increments, with a unique constraint on the EventId, VolunteerId pair. Use the EventVolunteerId (identity) as the foreign key to the Shift table. This enforces the constraint you'd like fairly simply, whilst normalizing your data somewhat.
I understand this is not the answer to your general question, however I'd see this as the best solution to your specific problem.
Edit:
I should have read the question fully. This solution will prevent one volunteer from doing two shifts at the same event, even if they don't overlap. Perhaps moving the shift start and end times to the EventVolunteer and having the check constraint on times on that table would suffice, though then you have shift data outside the Shift table which does not sound intuitive to me.
There is a way to do it by using triggers, which i wouldn't recommend. I would recommend not putting your buisness logic at the database level. The db doesn't need to know who, is staffing a certain shift at which time. That logic should be put in your buisness layer. I would recommend using a repository construction pattern. Scott gutherie has a very good chapter in his mvc 1.0 book which describes this (Link below).
http://weblogs.asp.net/scottgu/archive/2009/03/10/free-asp-net-mvc-ebook-tutorial.aspx

Resources