How can I insert a set of child records while updating the parent? - sql

I'm using SQL Server 2005 and wish to create a number address records, updating the contact records with the new Id's:
Take the following tables
create table contact(id int primary key identity, home_address_id int, work_address_id int)
create table address(id int primary key identity, street varchar(25), number int)
And foreign keys:
ALTER TABLE dbo.contact ADD CONSTRAINT FK_contact_address1 FOREIGN KEY (home_address_id) REFERENCES dbo.address(id)
ALTER TABLE dbo.contact ADD CONSTRAINT FK_contact_address2 FOREIGN KEY (work_address_id) REFERENCES dbo.address(id)
some dummy data
insert into contact default values
insert into contact default values
insert into contact default values
How can I insert a default empty address record for all contacts who have no home address, and update the home_address_id in one go?
The first part is simple:
insert into address(street) select null from contact where home_address_id is null
I can even get the newly create address id's:
declare #addressTable table(id int)
insert into address(street)
OUTPUT INSERTED.Id INTO #addressTable
select null from contact where home_address_id is null
Here's the new id's
select * from #addressTable
But how to update the contact table with these new Id's?

If possible, I would suggest normalizing your database by adding a Contact_Addresses table:
CREATE TABLE Contact_Addresses
(
contact_id INT NOT NULL,
address_id INT NOT NULL,
address_type VARCHAR(10) NOT NULL,
CONSTRAINT PK_Contact_Addresses PRIMARY KEY CLUSTERED (contact_id, address_id, address_type),
CONSTRAINT FK_ContactAddresses_Contacts (contact_id) REFERENCES Contacts (id),
CONSTRAINT FK_ContactAddresses_Addresses (address_id) REFERENCES Addresses (id),
CONSTRAINT CK_ContactAddresses_address_type CHECK address_type IN ('HOME', 'WORK')
)
Next, I would suggest not putting "dummy" records in your database. It's going to end up causing headaches down the road. The database should contain an accurate record of the data in your system. If you want to display some value by default when no address exists in the system for a contact then handle that in your UI.
If you really must though, then the following code should do the trick:
;WITH C_CTE AS
(
SELECT
id,
home_address_id,
ROW_NUMBER() OVER(ORDER BY id) AS seq
FROM
Contacts
),
(
SELECT
id,
ROW_NUMBER() OVER(ORDER BY id) AS seq
FROM
Addresses
)
UPDATE
C_CTE
SET
home_address_id = A.id
FROM
C_CTE C
INNER JOIN A_CTE A ON A.seq = C.seq

I would do it from the moment you get a new contact, thusly:
[receive contact information]
//prior to inserting contact
declare #homeAddress int, #workAddress int
[insert home address here (real or default based on input)]
set #homeAddress = ##Identity
[insert work address here (real or default)]
set #workAddress = ##Identity
[insert contact here referencing #homeAddress & #workAddress]
For the stuff already in your table, you're going to have to associate all of your null value ids to a contact id. Or, you could clear out your null value addresses, and modify the above statement to an update somehow (brain's not working at the moment, so all I'm coming up with is a cursor, and cursors are evil).

Related

SQL - cannot add or update a child row: a foreign key constraint fails

I keep getting the error:
Cannot add or update a child row: a foreign key constraint fails
(myWork.Bookingss, CONSTRAINT Bookingss_ibfk_1 FOREIGN KEY
(CustomersID) REFERENCES Customers (CustomersID))
I have spent a while researching as this is apart of a school assignment and can not seem to resolve the problem.
Here is my SQL code:
USE myWork ;
DROP TABLE IF EXISTS Bookingss ;
DROP TABLE IF EXISTS Customers ;
CREATE TABLE myWork.Customers
(
CustomersID INT NOT NULL AUTO_INCREMENT,
Surname CHAR(30) NOT NULL ,
FirstName CHAR(30) NOT NULL ,
Title CHAR(10),
DOB DATE,
HouseNumber INT,
StreetName CHAR(30),
Town CHAR(30),
PostCode CHAR(9),
Telephone INT,
PRIMARY KEY (CustomersID)
) ;
CREATE TABLE myWork.Bookingss
(
BookingsID INT NOT NULL AUTO_INCREMENT,
CustomersID INT NOT NULL,
AdultsBooked INT NOT NULL,
ChildrenBooked INT NOT NULL,
Check_In DATE,
Check_Out DATE,
PRIMARY KEY (BookingsID),
FOREIGN KEY (CustomersID)
REFERENCES myWork.Customers (CustomersID)
) ;
SHOW TABLES ;
INSERT INTO myWork.Bookingss ( CustomersID, AdultsBooked , ChildrenBooked , Check_In , Check_Out )
VALUES ("1", "2", "3", "2022-04-10", "2022-04-13" ) ;
INSERT INTO myWork.Customers ( Surname , FirstName , Title , DOB )
VALUES ( "smith" , "ryan" , "Mr" , "1998-02-16" ) ;
SELECT * FROM Customers , Bookingss;
You got the order wrong and some challenges to overcome. You are trying to insert a booking first, and this with an id for a customer that does not yet exist in the customer table at that moment.
You have to insert the customer first, then use the customer's id to insert the booking for the customer (using his id).
And there is the first challenge. The customer id is an auto increment field. You would not know the id when you insert a customer in the table.
You would have to fetch the customer and use the id of that customer to insert a booking for the customer. How do you fetch the customer? Selecting the customer with a specific name surname and first name is not a correct choice, any other field (or composition of fields) that I see in the table definition is not a good choice neither.
You'd need to think further about a good solution. There are several options I could tell you about. But the appropriate solution depends on your assignment/context.
By the way, the last select clause may not deliver the result you expect. You are producing a cartesian product (every row of the first table with every row of the second table). What you probably want is a JOIN where you link the first table with the second table accordingly (e.g. using the primary key and the foreign key).

SQL Server : create a foreign key with a condition

I'm designing a new database for a company, trying to keep strict constraints with foreign keys etc for integrity. I have a table [Member] which holds companies on the system. This table has a column of [internalContact] for the user in our company who deals with this member which has a foreign linked to the users table by user id.
What I would like to know is if it is possible to assign a condition to the foreign key, since the users table contains internal and external users. ie. for the field to only accept a user id where the user type is 5. Can this be done, or can I only control this in my application code?
Thanks
You can use a check constraint for this.
(The code is untested some syntax errors will be in there)
CREATE TABLE Member
(
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
InternalContactId
CONSTRAINT chk_Person CHECK (isInternalUser(internalContactId) > 0)
)
ALTER TABLE Member
ADD FOREIGN KEY (InternalContacId)
REFERENCES Persons(P_Id)
Then just create a function isInternalUser that returns 1 if user in ok to be an internal contact
CREATE FUNCTION isInternalUser ( #userId int(10) )
RETURNS int
AS
BEGIN
DECLARE #tmp int
SELECT #tmp = count(*)
FROM users
WHERE userId = #UserId and <check to see if user is internal>
RETURN(#CtrPrice)
END
GO

SQL Server 2008 Foreign Keys that are auto indexed

Are Foreign Keys in SQL Server 2008 are automatically indexed with a value? For Example. if I add a value in my Primary key (or auto incremetend) in may parent table will the table that has a foreign key referenced to that key will automatically have the same value? or I Have to do it explicitly?
No, if you create a foreign key in a child table, it will not automatically get populated when a parent row gets inserted. If you think about this it makes sense. Let's say you have a table like:
CREATE TABLE dbo.Students
(
StudentID INT IDENTITY(1,1) PRIMARY KEY,
Name SYSNAME
);
CREATE TABLE dbo.StudentLoans
(
LoanID INT IDENTITY(1,1) PRIMARY KEY,
StudentID INT FOREIGN KEY REFERENCES dbo.Students(StudentID),
Amount BIGINT -- just being funny
);
What you are suggesting is that when you add a row to Students, the system should automatically add a row to StudentLoans - but what if that student doesn't have a loan? If the student does have a loan, what should the amount be? Should the system pick a random number?
Typically what will happen in this scenario is that you'll be adding a student and their loan at the same time. So if you know the loan amount and the student's name, you can say:
DECLARE
#Name SYSNAME = N'user962206',
#LoanAmount BIGINT = 50000,
#StudentID INT;
INSERT dbo.Students(Name)
SELECT #Name;
SELECT #StudentID = SCOPE_IDENTITY();
INSERT dbo.StudentLoans(StudentID, Amount)
SELECT #StudentID, #LoanAmount;

need help in primary key and foreign key

I need help in auto populating the primary key values in foreign key table while inserting data in foreign key table. For Example: I have created table:
create table Patient
(
PatientId int IDENTITY(1,1) primary key,
FirstName varchar(50),
SurName varchar(50),
Gender char(20),
)
Say 5 rows are there in this Patient Table:
Say First Row value is: 1, Priya, Kumari, Female
I have created the Guardians Table:
create table Guardians
(
GuardiansId int identity(1,1) primary key,
PatientId int foreign key references Patient(PatientId),
FirstName varchar(50),
SurName varchar(50),
Gender char(20),
RelationToPatient varchar(50),
)
In this table Insert operations are like this:
insert into Guardians(FirstName, SurName, Gender,RelationToPatient)values('Sumit','Kumar','Male','Wife')
While selecting the Guardians Table PatientId showing NULL values: My query is while inserting the values in Guardians Table PatientId should be auto Populated which will come from Patient Table...
My second problem is: How to create the Identity column as varchar. For example: suppose I want to increment my Guardians Table with 'GRD0001', 'GRD0002', 'GRD0003' like this...
Thanks,
S.D
Your question is not very clear - what exactly do you want to do??
When you insert something into the Guardians table, you want to automatically also insert it into the Patients table? I don't quite follow. Can you make a complete example, maybe??
If you need to capture the insert IDENTITY value from the Patient table, do this:
DECLARE #NewPatientID INT
INSERT INTO dbo.Patient(fields) VALUES(.......)
SET #NewPatientID = SCOPE_IDENTITY()
INSERT INTO dbo.Guardians(PatientId, ......) VALUES(#NewPatientID, ......)
As for your second question: leave you GuardiansId IDENTITY as it is (only an INT column can be an IDENTITY and you want to keep that - trust me!) and add a computed column to your table:
ALTER TABLE dbo.Guardians
ADD GuardianIDWithPrefix AS
'GDR' + RIGHT('0000' + CAST(GuardiansId AS VARCHAR(4)), 4) PERSISTED
Since it's a PERSISTED field, you can even index on it and use it like a normal field in every respect.
That should do the trick!

SQL Server: Extracting a Column Into a Table

I have a table with a column that I want to extract out and put into a separate table.
For example, lets say I have a table named Contacts. Contacts has a column named Name which stores a string. Now I want to pull out the names into another table named Name and link the Contact.Name column to the Id of the Name table.
I can only use SQL to do this. Any ideas on the best way to go about this?
Let me know if I can clarify anything, thanks!
[edit]
One problem is that different contacts can be tied to the same name. So when different contacts have the same name and it gets exported the Name table would only have one unique row for that name and all the contacts would point to that row. I guess this wouldn't make sense if I were actually working on a contact book, but I'm just using it to illustrate my problem.
CREATE TABLE Name (NameID int IDENTITY(1, 1), [Name] varchar(50))
INSERT INTO Name ([Name])
SELECT DISTINCT [Name]
FROM Contact
ALTER TABLE Contact
ADD COLUMN NameID int
UPDATE Contact
SET NameID = [Name].NameID
FROM Contact
INNER JOIN [Name]
ON Contact.[Name] = [Name].[Name]
ALTER TABLE Contact
DROP COLUMN [Name]
Then add foreign key constraint, etc.
Create the new table with a Foreign key that points back to the contact table. Then insert the names and contactids from the contact table into this new table. After that you can drop the "name" column from the contact table.
CREATE TABLE Name
(
ContactId int,
Name nvarchar(100)
);
INSERT Name(Name)
SELECT ContactId, Name From Contact;
ALTER TABLE Contact
DROP Column name;
EDIT: Since you have edited the question to mention that one name can be associated with multiple contacts, this changes things in the opposite way.
CREATE TABLE Name
(
NameId int IDENTITY,
Name nvarchar(100)
);
INSERT Name(Name)
SELECT DISTINCT Name From Contact;
ALTER TABLE Contact
ADD NameId int;
UPDATE c
SET c.NameId = n.NameId
FROM Contact c
JOIN Name n on n.Name = c.Name;
ALTER Table Contact
Drop Column Name;
NOTE: Make sure that you create the appropiate foreign key between the Contact and Name tables using the NameId on the Contact table and also create a UNIQUE constraint on the "name" column in the Name table.
insert into another_table( contact_id, name )
select id, name
from contacts;
insert into new_table (contact_id, name)
select min(id), name
from contacts
group by name;
This is one way of ensuring only one row per name - you can substitute other functions for min (like, for eg max).
I'm not too sure why you would want to do this, though. No matter what, you will end up with some contacts that don't have a name linked to them...
ALTER TABLE `Contacts` ADD `name_id` INT( 12 ) NOT NULL
ALTER TABLE `Name` ADD `Name` VARCHAR( 200 ) NOT NULL
INSERT INTO Name (id, name) SELECT id, Name FROM Contacts
ALTER TABLE `Contacts` DROP `Name`
The problem is the name_id field, which is filles with "0" and should be have the same value as the id in the Contacts-Table. Here you can use the LOOP or ITERATE statement (if you using MySQL).