copying values with one to many relationship from external file in PostgreSQL - sql

Let say I want to copy new values from external sql file. The values are representing tables with one-to-many relationship, like: books(book_id, title, author_id, subject), author(author_id, name, field, status). The ids are starting from 1 and incrementing. But in the database there are already data/values. So how can I copy the new values, so that they get the right id values and keeping the relationship?
Thanks

One way would be to load the new data into temporary tables new_author and new_book, then insert rows from new_author into author where they didn't already exist, and then insert from new_book to book, using the ids from the newly created author records.
create temp table new_book(like book);
create temp table new_author(like author);
-- load data into new_book and new_author
\copy new_author from ~/new_author_file
\copy new_book from ~/new_book_file
insert into author (name, field, status)
select name, field, status
from new_author
where name not in (select name from author);
insert into book (title, author_id, subject)
select b.title, a.author_id, b.subject
from new_book b
join new_author na on b.author_id = na.author_id
join author a on na.name = a.name;
All of this assumes that author.name is unique, and that you know that you need to add all of the books.

Related

Conditionally insert multiple data rows into multiple Postgres tables

I have multiple rows of data. And this is just some made up data in order to make an easy example.
The data has name, age and location.
I want to insert the data into two tables, persons and locations, where locations has a FK to persons.
Nothing should be inserted if there already is a person with that name, or if the age is below 18.
I need to use COPY (in my real world example I'm using NPQSQL for .NET and I think that's the fastest way of inserting a lot of data).
So I'll do the following in a transaction (not 100% sure on the syntax, not on my computer right now):
-- Create a temp table
DROP TABLE IF EXISTS tmp_x;
CREATE TEMPORARY TABLE tmp_x
(
name TEXT,
age INTEGER,
location TEXT
);
-- Read the data into the temp table
COPY tmp_x (name, age) FROM STDIN (FORMAT BINARY);
-- Conditionally insert the data into persons
WITH insertedPersons AS (
INSERT INTO persons (name, age)
SELECT name, age
FROM tmp_x tmp
LEFT JOIN persons p ON p.name = tmp.name
WHERE
p IS NULL
AND tmp.age >= 18
RETURNING id, name
),
-- Use the generated ids to insert the relational data into locations
WITH insertedLocations AS (
INSERT INTO locations (personid, location)
SELECT ip.id, tmp.location
FROM tmp_x tmp
INNER JOIN insertedPersons ip ON ip.name = tmp.name
),
DROP TABLE tmp_x;
Is there a better/easier/more efficient way to do this?
Is there a better way to "link" the inserts instead of INNER JOIN insertedPersons ip ON ip.name = tmp.name. What if name wasn't unique? Can I update tmp_x with the new person ids and use that?

Copying data from one table to another different column names

I'm having an issue copying one table's data to another. I have around 100 or so individual tables that have generally the same field names but not always. I need to be able to copy and map the fields. example: source table is BROWARD and has column names broward_ID, name, dob, address (the list goes on). The temp table I want to copy it to has ID, name, dob, address etc.
I'd like to map the fields like broward_ID = ID, name = name, etc. But many of the other tables are different in column name, so I will have to write a query for each one. Once I figure out the first on, I can do the rest. Also the column in both tables are not in order either..thanks in advance for the TSQL...
With tables:
BROWARD (broward_ID, name, dob, address) /*source*/
TEMP (ID, name, address,dob) /*target*/
If you want to copy information from BROWARD to TEMP then:
INSERT INTO TEMP SELECT broward_ID,NAME,ADDRESS,DOB FROM BROWARD --check that the order of columns in select represents the order in the target table
If you want only copy values of broward_ID and name then:
INSERT INTO TEMP(ID, name) SELECT broward_ID,NAME FROM BROWARD
Your question will resolve using update
Let's consider we have two different table
Table A
Id Name
1 abc
2 cde
Table B
Id Name
1
2
In above case want to insert Table A Name column data into Table B Name column
update B inner join on B.Id = A.Id set B.Name = A.Name where ...

Using output to capture auto increment identity, plus past identity mapping table

I am trying to make exact copies of data in SQL, with new clientIDs, but keep the existing data in the old client as well. I will be inserting the data into a table with an auto incrementing integer primary key ID. I need to retain the ID's of the Old records and the new records together so I can continue using this mapping as I copy the different table data so I can maintain relationships.
At this point I have the following:
INSERT INTO [dbo].[Driver]
OUTPUT inserted.ID
inserted.Name
inserted.ClientID
SELECT Name,
1234 AS clientID
FROM dbo.Driver
I am wondering if there is a way to also select the old ID of the driver in the Output so I can then insert all of this into a holding table using the OUTPUT. So I need to end up with the following after I perform the insert into the dbo.Driver table so I can also insert these values into a temp table:
NewID
OldID
Name
ClientID
At this point I don't know of a way to pull the Original ID from the original record.
I ended up using MERGE INTO to keep track of the old ID as per the following SO post:
How to use OUTPUT to capture new and old ID?
you can try...
INSERT INTO dbo.Driver (oldID, Name, clientID)
SELECT
B.ID,
A.Name,
1234 AS clientID
FROM dbo.Driver A
LEFT JOIN dbo.Driver B ON A.Name = B.Name AND A.clientID = b.clientID
or maybe just
INSERT INTO dbo.Driver (oldID, Name, clientID)
SELECT
ID,
Name,
1234 AS clientID
FROM dbo.Drive

Transact-SQL: Getting ID of Lookup Value if Data isn't Related

I have 3 tables and two are related (Name and Gender):
Staging(id, name, gender)
Name(id, name genderID)
Gender(id, gender)
The data has been "dumped" into Staging(id, name, gender) in a denormalized fashion and now I'm trying to normalize the data.
I need to be able to use t-sql to do the following
Insert the name from the Staging table into the Name table
Get the id from the Gender table and insert into the Name table as a foreign key
The problem is the Gender and Name tables aren't related to Staging so I'm trying to understand the logic of how this transaction should work.
My assumption was that I needed to somehow to an INSERT INTO SELECT with some type of subquery, but I'm just at a lost. Thanks.
Yes, you need INSERT INTO...SELECT. Join table staging with Gender via column gender so you can get the ID.
INSERT INTO Name (ID, Name, GenderID)
SELECT s.id, s.name, g.id
FROM Staging s
INNER JOIN Gender g
ON s.gender = g.gender

Performing multiple inserts for a single row in a query

I have a table containing data that i need to migrate into another table with a linking table. This is a one time migration as part of an upgrade.
I have a company table that contains records relating to a company and a contact person.
I want to migrate the contact details into another table and link the new person with a linking table
Consider I have this table which is already populated
tblCompany
CompanyId
CompanyName
RegNo
ContactForename
ContactSurname
And i want to migrate the contact person data to
tblPerson
PersonID (identitycolumn)
Forename
Surname
and use the identity column resulting and insert it into the linking table
tblCompanyPerson
CompanyId
PersonId
I've tried a few different ways to approach this using cursors and output variables into a temp table but none seem right to me (or give me the solution...)
The closest i have got is to have a companyID on tblPerson and insert companyId into it and output the new personId and the companyId into a temp table. Then loop through the temp table to create the tblCompanyContact.
example
declare #companycontact TABLE (companyId int, PersonId int)
insert into tblPerson
(Forename,
Surname,
CompanyID)
output inserted.CompanyID, INSERTED.PersonID into #companycontact
select
ContactPersonForeName,
ContactPersonSurename,
CompanyID
from tblCompany c
insert into tblCompanyPerson
(CompanyID,
PersonID)
select c.companyId, PersonId from #companycontact c
Background
Im using MS SQL Server 2008 R2
The tblPerson is already populated with hundreds of thousands of
records.
There is a 'trick' using MERGE statement to achieve mapping between newly inserted and source values:
MERGE tblPerson trgt
USING tblCompany src ON 1=0
WHEN NOT MATCHED
THEN INSERT
(Forename, Surename)
VALUES (src.ContactPersonForeName, src.ContactPersonSurename)
OUTPUT src.CompanyID, INSERTED.PersonID
INTO tblCompanyPerson (CompanyId, PersonID);
That 1=0 condition is to always get everything from source. You might want to replace it or even whole source with some sub-query to actually check whatever you already have same person mapped.
EDIT: Here is some reading about using MERGE and OUTPUT
Because I don't know what SQL you are using its difficult to decide if this is correct. i also don't know if you already tried this but it's the best idea i have:
insert into tblPerson
(Forename, Surename)
Select ContactForename, ContactPersonSurename
from tblCompany
insert into tblCompanyPerson
(CompanyID, PersonID)
select CompanyId, PersonID
from tblPerson, tblCompany
where ContactForename = Forename and ContactPersonSurename = Surename
Sarajog