SQL Server: Extracting a Column Into a Table - sql

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).

Related

postgresql how to update another table while on conflict

I have two tables
CREATE TABLE people (
name VARCHAR(100) NOT NULL,
company_id int8 NOT NULL,
);
CREATE TABLE company (
id int8 NOT NULL,
);
I want to copy data from csv to DB. This is my script
BEGIN
CREATE TEMP TABLE tmp_company
ON COMMIT DROP AS SELECT * FROM company WITH NO DATA;
\COPY tmp_company FROM 'company.csv' WITH CSV HEADER
DELIMITER as ',';
INSERT INTO company
SELECT * FROM tmp_company
ON CONFLICT DO NOTHING;
CREATE TEMP TABLE tmp_people
ON COMMIT DROP AS SELECT * FROM people WITH NO DATA;
\COPY tmp_people FROM 'people.csv' WITH CSV HEADER
DELIMITER as ',';
INSERT INTO people
SELECT * FROM tmp_people
ON CONFLICT DO NOTHING;
COMMIT;
If existing company id is found in company table,
I should do company.id+=1 and replace the new company_id for the related people records.
Example:
company.csv
id
1
5
people.csv
name,company_id
tom,1
paul,5
existing company table data
id
1
2
existing people table data
name,company_id
tom,1
paul,2
After copying data from csv to DB, the data should look like
company table data
id
1
2
3 <-- from csv data, as 1,2 are used, set id=3
5
people table data
name,company_id
tom,1
paul,2
tom,3 <-- from csv data
paul,5 <-- from csv data
How can i do this? I am wondering if I can add the logic after ON CONFLICT...
Edit 1:
The two tables' size are near 5TBs.
and two csv contains 5M records.
First, you should use the bigserial data type instead of int8 for the column id of the table company so that to automatically increase the id when inserting a new row.
Then, you should create a foreign key between your tables people and company with the option ON UPDATE CASCADE so that any change in the id column of the table company will be automatically propagated to the company_id column in the table people.
CREATE TABLE company (
id bigserial NOT NULL
);
CREATE TABLE people (
name VARCHAR(100) NOT NULL,
company_id int8 NOT NULL,
CONSTRAINT fkey FOREIGN KEY company_id REFERENCES company(id) ON UPDATE CASCADE
);

SQL Server trigger can't insert

I beginning to learn how to write trigger with this basic database.
I'm also making my very 1st database.
Schema
Team:
TeamID int PK (TeamID int IDENTITY(0,1) CONSTRAINT TeamID_PK PRIMARY KEY)
TeamName nvarchar(100)
History:
HistoryID int PK (HistoryID int IDENTITY(0,1) CONSTRAINT HistoryID_PK PRIMARY KEY)
TeamID int FK REF Team(TeamID)
WinCount int
LoseCount int
My trigger: when a new team is inserted, it should insert a new history row with that team id
CREATE TRIGGER after_insert_Player
ON Team
FOR INSERT
AS
BEGIN
INSERT INTO History (TeamID, WinCount, LoseCount)
SELECT DISTINCT i.TeamID
FROM Inserted i
LEFT JOIN History h ON h.TeamID = i.TeamID
AND h.WinCount = 0 AND h.LoseCount = 0
END
Executed it returns
The select list for the INSERT statement contains fewer items than the insert list. The number of SELECT values must match the number of INSERT columns.
Please help thank. I'm using SQL Server
The error text is the best guide, it is so clear ..
You try inserting one value from i.TeamID into three columns (TeamID,WinCount,LoseCount)
consider these WinCount and LoseCount while inserting.
Note: I Think the structure of History table need to revisit, you should select WinCount and LoseCount as Expressions not as actual columns.
When you specify insert columns, you say which columns you will be filling. But in your case, right after insert you select only one column (team id).
You either have to modify the insert to contain only one column, or select, to retrieve 3 fields as in insert.
If you mention the columns where values have to be inserted(Using INSERT-SELECT).
The SELECT Statement has to contain the same number of columns that have been specified to be inserted. Also, ensure they are of the same data type.(You might face some issues otherwise)

SQL Server 2008 stored procedure for a table having two foreign keys and both of them is a composite key

I have the following tables :
create table ApartmentInfo(
ApartmentId int primary key identity,
ApName nvarchar(50))
create table [User](
UserId int primary key identity,
FirstName nvarchar(50),
LastName nvarchar(50),
Username nvarchar(50),
[Password] nvarchar(50),
[Description] nvarchar(200))
create table ApUser(
ApartmentId int foreign key references ApartmentInfo(ApartmentId),
UserId int foreign key references [User](UserId),
primary key(ApartmentId,UserId))
Summary of the usage is: suppose I have 10 apartments in my Apartmentinfo table and 3 users in the [User] table.
Now I want to write a stored procedure such that:
every UserId has all 10 ApartmentId's, and whenever a new apartment is created in ApartmentInfo table it will also be added in ApUser table again having all 3 userId's.
And if a new user is created in User table then it will also have all the 10 ApartmentId's related with it in ApUser table.
Thanks in advance, I am new to SQL Server and I don't know how it can be done or not but, if it is possible then please let me know, I will be grateful to you, thanks.
If you're not dead set on using a stored procedure this use case seems like an excellent fit for server side triggers:
IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[trigger_insert_apartment]'))
DROP TRIGGER [dbo].[trigger_insert_apartment]
GO
CREATE TRIGGER trigger_insert_apartment
ON ApartmentInfo FOR INSERT
AS INSERT ApUser(ApartmentId, UserId ) SELECT i.ApartmentId, u.UserId FROM [User] AS u, inserted AS i
GO
IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[trigger_insert_user]'))
DROP TRIGGER [dbo].[trigger_insert_user]
GO
CREATE TRIGGER trigger_insert_user
ON [User] FOR INSERT
AS INSERT ApUser(ApartmentId,UserId ) SELECT a.ApartmentId, i.UserId FROM ApartmentInfo AS a, inserted AS i
GO
These two triggers will insert all apartments or users into the apinfo table when you add a new user or apartment, and if I understood your question that was what you wanted?
To address your question about what you'd do to associate apartments with users IF YOU WANTED TO LATER...
If you're restricting which apartments a user has access to view, I would NOT associate them individually to every user. BUT, if they're manually adding a "watch" on an apartment in your application, then this is ok to do. If you want to only show them a certain set of apartments, try to group them in a logical way if you can. So maybe have an ApartmentGroup lookup table:
ApartmentGroup(ApartmentGroupId (Int), ApartmentGroupName varchar(50))
and assign an ApartmentGroupId to each apartment. Then you can have a join table for ApUser and ApartmentGroup. That way you're not associating every apartment with every user.
However, if you want to add the apartment to each user still, then in your InsertApartment stored procedure, just insert it into the ApUser table as well.

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

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).

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!