Always bet on requirements changing, or being reinterpreted, at some point in the future.
Does the requirements documentation explicitly state that there are exactly two or just that there should be up to one primary and up to one secondary? If the latter then what could be meant is there could be many addresses of which up to one is the primary and up to one is the secondary and you should allow for more addresses.
Even if not, I would still err on the side of addresses being separate entities if you need to support more than one for any other given entity. The extra work now should be minimal, if you need to emulate the non-normal-form for the application logic you can by various means, and when the requirements change later your job will be a lot easier (and you can either impress the client/devs with your foresight or keep shtum and charge them as if the change request does require significant structural alterations in the DB).
Given your description of what you are trying to do, I would take the below approach. I've put together a rather simplistic example for you, but I'm sure that you can modify it to suit your needs.
It assumes a single base table and then puts that data into three separate tables. The last table does a lookup into the Company and Address tables to get the ID's for the foreign keys.
/** Setup our Relational destination. Normally would have Foreign Keys*/
DECLARE @AddressTable TABLE
(
AddressID INT NOT NULL PRIMARY KEY IDENTITY(1, 1)
, AddressLine1 VARCHAR(100) NULL
, City VARCHAR(100) NULL
, [State] CHAR(2) NULL
, ZipCode VARCHAR(10) NULL
);
DECLARE @CompanyTable TABLE
(
CompanyID INT NOT NULL PRIMARY KEY IDENTITY(1, 1)
, CompanyName VARCHAR(100) NOT NULL
);
DECLARE @ContactTable TABLE
(
ContactID INT NOT NULL PRIMARY KEY IDENTITY(1, 1)
, PersonName VARCHAR(100) NULL
, Email VARCHAR(50) NULL
, PhoneNumber VARCHAR(10) NULL
, CompanyID INT NULL
, AddressID INT NULL
);
/** Setup Our Source Data*/
DECLARE @SourceTable TABLE
(
CompanyName VARCHAR(100) NULL
, PersonName VARCHAR(100) NULL
, Email VARCHAR(50) NULL
, PhoneNumber VARCHAR(10) NULL
, AddressLine1 VARCHAR(100) NULL
, City VARCHAR(100) NULL
, [State] CHAR(2) NULL
, ZipCode VARCHAR(10) NULL
);
INSERT INTO @SourceTable
(
CompanyName
, PersonName
, Email
, PhoneNumber
, AddressLine1
, City
, [State]
, ZipCode
)
VALUES (
'FunkyTown'
, 'Jon Smith'
, 'jsmith@funkytown.com'
, '1234567890'
, '123 Funky'
, 'Town'
, 'VA'
, '63487'
)
, (
'Microsoft'
, 'Doe Really'
, 'dreally@notmicrosoft.com'
, '0987654321'
, '123 Red'
, 'MS-Ville'
, 'CA'
, '90210'
)
, (
'FunkyTown'
, 'Jane Awesome'
, 'jawesome@funkytown.com'
, '1234567890'
, '123 Funky'
, 'Town'
, 'VA'
, '63487'
);
/** Perform Our Inserts
- Start with the tables without any Foreign Key Constraints
- Make sure to only add distinct data.
- Note also the self joins onto the destination table to prevent duplicates
*/
INSERT INTO @CompanyTable (CompanyName)
SELECT DISTINCT
CompanyName
FROM @SourceTable
WHERE NOT (CompanyName IN (SELECT CompanyName FROM @CompanyTable));
INSERT INTO @AddressTable (AddressLine1, City, [State], ZipCode)
SELECT DISTINCT
D.AddressLine1
, D.City
, D.[State]
, D.ZipCode
FROM @SourceTable AS D
LEFT OUTER JOIN @AddressTable AS A ON A.AddressLine1 = D.AddressLine1
AND A.City = D.City
AND A.[State] = D.[State]
AND A.ZipCode = D.ZipCode
WHERE A.AddressID IS NULL;
INSERT INTO @ContactTable (PersonName, Email, PhoneNumber, CompanyID, AddressID)
SELECT D.PersonName
, D.Email
, D.PhoneNumber
, C.CompanyID
, A.AddressID
FROM @SourceTable AS D
LEFT OUTER JOIN @CompanyTable AS C ON C.CompanyName = D.CompanyName
LEFT OUTER JOIN @AddressTable AS A ON A.AddressLine1 = D.AddressLine1
AND A.City = D.City
AND A.[State] = D.[State]
AND A.ZipCode = D.ZipCode
LEFT OUTER JOIN @ContactTable AS D2 ON D2.PersonName = D.PersonName
AND D2.Email = D.Email
AND D2.PhoneNumber = D.PhoneNumber
AND D2.CompanyID = C.CompanyID
AND D2.AddressID = A.AddressID
WHERE D2.ContactID IS NULL;
/** Display Results */
SELECT CompanyID
, CompanyName
FROM @CompanyTable;
SELECT AddressID
, AddressLine1
, City
, [State]
, ZipCode
FROM @AddressTable;
SELECT ContactID
, PersonName
, Email
, PhoneNumber
, CompanyID
, AddressID
FROM @ContactTable;
Best Answer
You just have to traverse the relationship backwards, deleting from the last child first, then the next child, and so on.
Let's say you want to delete all the data for a specific customer, you could have this procedure:
For the delete from a relation, there are several ways to do it.
IN
is arguably the most straightforward, but you can also delete from a join:Or using
EXISTS
:You could even use
MERGE
, but I'm not going to show you how, because I think it's often a terrible idea. :-)