2

I would like to vertically split an existing (already filled with data).

Say my starting table (trip) looks like this:

  ID    Person        Age       Trip_to           Date      
 ...     Alice        21        Los Angeles       2015-04-01     
 ...     Bob          35        New York          2015-03-15   
 ...     Bob          35        Chicago           2015-03-20 
 ...     Bob          35        San Francisco     2015-03-29 
 ...     Carol        29        Miami             2015-03-30 
 ...     Carol        29        Boston            2015-04-05

I would like to split this table into two tables, as it should be, one each for person and trip.

When copying each unique person into a table person, I want the automatically created uniqueidentifier column in that table person.pId (the primary key) to be copied into a newly created uniqueidentifier column trip.personid in the original table and turn that into a foreign key. (I would then delete the trip.person and trip.age column from the original table, and have the data structured as I want them.)

I thought I could use a trigger for that when I insert the person rows into the new table, like so:

CREATE TRIGGER tr_person
        on person
        after INSERT
        as
        begin
       UPDATE Trip 
       SET personId=(SELECT i.pId from inserted i)
       WHERE person=(SELECT i.person from inserted i) and age=(SELECT i.age from inserted i)
END

However, I get this:

    Subquery returned more than 1 value. 
This is not permitted when the subquery follows =, !=, <, <= , >, >= 
or when the subquery is used as an expression.

Apparently the trigger only executes after all (potentially multiple!) inserts have been carried out, and by that time a query on inserted is no longer allowed for my SET statement.

I also thought about using an OUTPUT clause on the INSERT statement, but that wouldn't work either

Insert into person (Driver, Age)
OUTPUT inserted.pId INTO trip.personId WHERE trip.person=inserted.person AND trip.Age=inserted.Age
(Select DISTINCT Person, Age FROM Trip)

So I am wondering now, am going about this all wrong? Is there another way to automagically create the keys and relationships between the two newly split tables?

EDIT Desired result: Table trip:

  ID    Trip_to           Date            PersonId
 ...     Los Angeles       2015-04-01     xxxx1
 ...     New York          2015-03-15     xxxx2
 ...     Chicago           2015-03-20     xxxx2 
 ...     San Francisco     2015-03-29     xxxx2 
 ...     Miami             2015-03-30     xxxx3 
 ...     Boston            2015-04-05     xxxx3

Table person

  pId    Person       Age         
 xxxx1   Alice        21         
 xxxx2   Bob          35              
 xxxx3   Carol        29 

(The ID fields should all be uniqueidentifiers, of course)

TVogt
  • 185
  • 1
  • 1
  • 11

1 Answers1

1

Here is an idea:

First, you insert all distinct Person from Trip table to Person table:

CREATE TABLE Person(
    pID UNIQUEIDENTIFIER PRIMARY KEY,
    Person VARCHAR(10),
    Age     INT
)

INSERT INTO Person
    SELECT 
        NEWID(), Person, Age
    FROM(
        SELECT DISTINCT Person, Age FROM Trip
    )t

Then, add a new FK column to Trip table PersonId which references pId from the Person table:

ALTER TABLE Trip 
    ADD PersonId UNIQUEIDENTIFIER 
    FOREIGN KEY(PersonId) REFERENCES Person(pId)

Then, UPDATE the newly added FK column with values from Person table using a JOIN on Person and Age:

UPDATE t
    SET PersonId = p.pID
FROM Trip t
INNER JOIN Person p
    ON p.Person = t.Person
    AND p.Age = t.Age

Finally, you can drop Person and Age from Trip table:

ALTER TABLE Trip DROP COLUMN Person
ALTER TABLE Trip DROP COLUMN Age

RESULT

Person

pID                                  Person     Age
------------------------------------ ---------- -----------
35815766-1634-45FF-A3F6-8194B43F3F65 Alice      21
8EB3A7CC-CED1-4DBC-98B0-99D325BC7F67 Bob        35
D0EDCEA8-3825-4693-9352-BF7A04AEFCB2 Carol      29

Trip

ID                                   Trip_to              Date       PersonId
------------------------------------ -------------------- ---------- ------------------------------------
77357A57-FAAE-43DE-923E-219038B8641E Los Angeles          2015-04-01 35815766-1634-45FF-A3F6-8194B43F3F65
C1B64E81-D30A-46A9-A868-1D92C4B64B8C New York             2015-03-15 8EB3A7CC-CED1-4DBC-98B0-99D325BC7F67
21F3614A-8E76-4A64-8A0B-815D5343FC26 Chicago              2015-03-20 8EB3A7CC-CED1-4DBC-98B0-99D325BC7F67
E1DB1926-4268-4BFA-B5E0-DA603DA8E1B7 San Francisco        2015-03-29 8EB3A7CC-CED1-4DBC-98B0-99D325BC7F67
F50E45E6-E689-444B-96C1-F936CA6F3D2A Miami                2015-03-30 D0EDCEA8-3825-4693-9352-BF7A04AEFCB2
C2FA7073-79D7-42E8-B2C8-6EEDBC374002 Boston               2015-04-05 D0EDCEA8-3825-4693-9352-BF7A04AEFCB2

Side note: You should not be storing the Age of the person. Instead, store the birthdate and compute the age on the fly.

Felix Pamittan
  • 31,544
  • 7
  • 41
  • 67
  • 1
    Cheers, that does indeed work. I tried to do it all simultaneously, but this is a lot clearer and more elegant. As for the `age`, I was just looking for a simple attribute for my example. The table I actually want to do this with has 50 or so columns and 250.000 rows. I imported a csv file, and now need to sort it out into multiple tables. Again, thanks a lot! – TVogt Jul 29 '15 at 00:12