Copying a 1-to-many relationship between two tables to another two tables (SQL 2005) - sql-server-2005

I have two tables with a parent-child relationship. I would like to copy some of their records to two other tables, also with a parent-child relationship, but with a slightly different table structure.
There is a foreign key involved with both sets of tables, an integer column. All tables have the identity increment on for their primary key columns.
If I do a SELECT INTO from the source parent table to the destination parent table, the primary key values for the destination records will be different from the source records, and the parent-child relationship will be lost.
Does anyone know how I can preserve this relationship during the copy, given that I'll have new primary key values in the new parent table? I'd prefer not to set the identity increment off for the new tables during this copy procedure, because there's no guarantee the primary key values in the source table won't already be in the destination.
Hope my description makes sense, and thanks for your opinions. Let me know if I can clarify further.

When I have done this, I did so by preserving the old ID after the insert into the new table.
Some people add a temporary column with the old ID, but I know of a better way.
You can use the OUTPUT clause to insert the inserted records into a temp table (with the new and the old IDs as a mapping table. Then join to that when inserting the child records.
Something like this (crude)
DECLARE #MyTableVar TABLE (
NewID INT,
OldID INT
)
INSERT NewParentTable
OUTPUT
INSERTED.ID,
old.ID
INTO #MyTableVar
SELECT *
FROM OldParentTable old
INSERT NewChildTable
SELECT
old.ID,
t.NewID,
old.name
FROM OldChildTable old
INNER JOIN #MyTableVar t ON t.OldID = old.ParentID

You'd have to maintain the original ID in a separate column in the table with the corresponding parentID.
So in your destination table, you'd need to add an parentID_orig column (not auto-number) to retain the link.

Related

How to determine the auto-generated primary key used as a foreign key for another table

This is a two-part question. Attached is a diagram for a PostgreSQL database table design. There are four tables. Table Main has a one-to-many relationship with table Submain. Table Submain has a one-many relationship with table Subsub. The primary keys for all four tables are serial NOT NULL (so they auto-increment). Each table has multiple attributes that are not shown here.
Question 1. Multiple users will access this application and database. When a user accesses an application that uses this database, some of their information will be stored in table Main. Subsequent information (provided by the user and other results based on the user's input) will be stored in tables Submain and Subsub. My thinking is as follows:
User submits information via a form.
A MainId primary key will be automatically generated and some of the user information will be placed in table Main.
A record will be inserted into table Submain based on the user's input (items in Main). How can I determine what the user's primary key MainId is so that I can insert it into Submain.MainId [FK] for the new record?
A record will also be inserted into Subsub and that information will be based on information in table Submain. Similarly, how can I determine Submain.Submain [PK] so that I can use it as the foreign key in Subsub.Submain [FK]?
Question 2. There is a many-to-many relationship between Main and Other (I left out the associative table). However, in order to insert a record into table Other, information is required from Subsub. There will be a one-to-one mapping between Subsub and Other. Do I need to draw out that one-to-one relationship OR can table Other be populated based on a complex SELECT/JOIN statement from table Main down to table Subsub? This might be a bad question, but I think that I need to draw a one-to-one relationship and insert a foreign key SubsubId [FK] into Other instead of trying a complicated SQL statement.
Answer to Q1: Use data-modifying CTEs and return the serial PK with the RETURNING clause:
WITH ins_main AS (
INSERT INTO main(col1)
VALUES ('some value 1')
RETURNING main_id
)
, ins_submain AS (
INSERT INTO submain (main_id, col2)
SELECT main_id, 'some value 2'
FROM ins_main
RETURNING submain_id
)
INSERT INTO subsub (submain_id, col3)
SELECT submain_id, 'some value 3'
FROM ins_submain;
Requires Postgres 9.1 or later.
Related answers with explanation and links:
Insert data in 3 tables at a time using Postgres
PostgreSQL store value returned by RETURNING

primary keys are shifted

Background Information
For some reason, while inserting a huge data into multiple tables from xml, my primary keys are shifted by an offset.. (Maybe because of multiple failed attempts :P)
I have two tables.. tableA and tableB. They are in one-to-may relationship.
tableA is the parent table and has the Primary key column ...say DataIndex.
now DataIndex has come out like this..
685, 686, 687... and so on.
and corresponding values present in the child table i.e TableB are the same.
Problem
How do I shift the values up so that DataIndex start from 1, 2, 3..and so on; In both tables ?
I'm assuming that the primary key is actually an identity column that auto-increments itself upon insertion. What you will need to do is 'reseed' the identity column. You can do this by renaming the table, create a duplicate table with the original name, then inserting the data from the old table to the new one (the primary key field will be reset and will auto-increment from 1 again). When doing the insertion, make sure to include the old primary key value as an additional column for reference in the other tables.
To match up the related table, all you will need to do is do an UPDATE and join to your new table on the old primary key value:
UPDATE tableB SET
PRIMARYKEYCOLUMN = tableA.PRIMARYKEYCOLUMN
FROM tableA
WHERE
tableA.OLDPRIMARYKEYCOLUMN = tableB.PRIMARYKEYCOLUMN
I would do the following:
construct a list of Foreign Keys pointing to your PK;
ALTER all FKs, adding ON UPDATE CASCADE clause. This step might not work for some databases, you might need to DROP and CREATE constraint again;
Find the smallest current PK values, like: SELECT min(id) FROM tableA;
Refresh PK values: UPDATE tableA SET id = id - min_id + 1;
Remove ON UPDATE CASCADE clause.
Note, that depending on the tableA size and the database engine you're using, it might be faster and easier to completely rebuild the table to avoid bloat of data files.
Remove PK
Walk through records eith DB CURSOR or write a script on any language sequentally reading and changing id's
Restore PK
Setup correct identity seed for the PK
Changing seed might involve identity column drop and recreate.

When adding data to a table, it doesn't show up in the Foreign key of the other table

I have a table COURSE which has two attributes COURSECODE (PK) and COURSENAME
I also have another table COURSEUNIT, which has three attributes COURSECODE (PK) (FK) UNITCODE (PK) (FK) and CREDIT .
When I add data to the table COURSE it doesn't add the data to the COURSEUNIT table. What is the problem?
Foreign key does not mean that you get one row for each key in the referenced table. It only means that any row in the COURSEUNIT table must reference an existing row in COURSE.
Those are 2 different tables and you have to manage the relation, you have to insert the data first in the course table then take the id that will be generated from this insert and make another insert in the coursecode table, they will not be inserted automatically.
No problem there, that's how SQL works, you'll need a second INSERT statement to populate the COURSEUNIT table.
You need to write the query to populate the second table, how else will it know what values besides the FK to put in it? Child tables do not auto populate in any database.

How to cascade update 2 tables whose foreign key is not set to cascade?

I got 2 tables. They have a foreign key relation. But the foreign key is not set to update cascade. Now I want to update the table's primary key. SQL Server always prevent me from doing that because of the FK. How could I do it in SQL command? I don't have the right to modify the FK.
Thanks.
Why would you want to do this? It strikes me as potentially hazardous.
Your SQL would need to update the related table first NULLing the links back to the PK table and storing the IDs of the records effected. Then you can update the PK in the PK table. Then go back and update the FKs in the related tables.
Create new rows based on the existing row's attribute values but using the new key value. Do the same for all referencing tables. Then, using the new old key value, delete rows from the referencing tables then the referenced table. Something like:
INSERT INTO Table1 (key_col, attrib_col1)
SELECT 'new_key_value', attrib_col1
FROM Table1
WHERE key_col = 'old_key_value';
INSERT INTO Table2 (key_col, attrib_col2)
SELECT 'new_key_value', attrib_col2
FROM Table2
WHERE key_col = 'old_key_value';
DELETE
FROM Table2
WHERE key_col = 'old_key_value';
DELETE
FROM Table1
WHERE key_col = 'old_key_value';
You should only need to INSERT new rows for the parent table and UPDATE the child rows after the fact...
INSERT INTO ParentTable (PKColumn, attribute1, attribute2)
SELECT 'NewPKValue', attribute1, attribute2
FROM ParentTable
WHERE PKColumn = 'OldPKValue'
;
UPDATE ChildTable
SET FKColumn = 'NewPKValue'
WHERE FKColumn = 'OldPKValue'
;
DELETE
FROM ParentTable
WHERE PKColumn = 'OldPKValue'
;
Now for the gotchas:
1.) The above code wont work if there are any unique indexes defined on non-PK columns in the parent table and you need to use the current records' non-PK data values without modification.
2.) Since you're asking this question, I'm assuming your Parent table is not using an IDENTITY column as the PK.
3.) The code is certainly less than efficient. If your db has to do this infrequently on a few rows, you should be fine. If you need to do this 80 times per second, then I would strongly suggest you talk to the programmer/DBA or vendor, if they're at all available, about updating the FK definition to include ON UPDATE CASCADE.
4.) Make sure that there aren't any triggers on either table that would lead to unintended side effects when you create the new parent records or update the child records.

How to move data between multiple database's table while maintaining foreign-key relationships/referential integrity?

I'm trying to figure out the best way to move/merge a couple tables of worth of data from multiple databases into one.
I have a schema similar to the following:
CREATE TABLE Products(
ProductID int IDENTITY(1,1) NOT NULL,
Name varchar(250) NOT NULL,
Description varchar(1000) NOT NULL,
ImageID int NULL
)
CREATE TABLE Images (
ImageID int IDENTITY(1,1) NOT NULL,
ImageData image NOT NULL
)
With a foreign-key of the Products' ImageID to the Images' ImageID.
So what's the best way to move the data contained within these table from multiple source databases into one destination database with the same schema. My primary issue is maintaining the links between the products and their respective images.
In SQL Server, you can enable identity inserts:
SET IDENTITY_INSERT NewTable ON
<insert queries here>
SET IDENTITY_INSERT NewTable OFF
While idenitity insert is enabled, you can insert a value in the identity column like any other column. This allows you to just copy the tables, for example from a linked server:
insert into newdb.dbo.NewTable
select *
from oldserver.olddb.dbo.OldTable
I preposition the data in staging tables (Adding a newid column to each). I add a column temporarily to the table I'm merging to that is Oldid. I insert the data to the parent table putting the currect oldid inthe oldid column. I use the oldid column to join to the staging table to populate the newid column in the staging table. Now I have the New FK ids for the child tables and ccan insert using them. If you have SQL server 2008, you can use the OUTPUT clause to return the old and newids to a temp table and then use from there rather than dding the column. I prefer, to have the change explicitly stored ina staging table though to troubleshoot issues in the conversion. At the end nullout the values inteh oldid column if you are then going to add records from a third database or drop it if you are done. Leave the staging tables in place for about a month, to make research into any questions easier.
In this case you could move the images and then move the products. This would ensure that any image a product has a reference to will already be in place.