I have a database with three tables, call them Sale
, Store
and Hub
. Each instance of Store
references an instance of Hub
, although there may be instances of Hub
not referenced in the Store
table. In most cases, an instance of Sale
will reference an instance of Store
(and therefore can be mapped to an instance of Hub
.) However in some cases a Sale
will reference the Hub
table directly.
This relationship models the following: stores are stocked by hubs (stores and hubs are both locations) and while most sales occur at stores, they can also occur at the hubs.
In a simplified version where the Sale
-Hub
relationship doesn't exist, I can easily make StoreID
part of the primary key for the Sale
table, which is what I'd like to do here, except that StoreID
will occasionally be null (when the sale occurs at a hub and not at a store.)
Therefore a sample from the Sale
table might look like this:
SaleID SaleDate StoreID HubID Quantity
1 2012-05-16 1 NULL 100
2 2014-09-27 2 NULL 99
3 2018-02-01 NULL 9 30
4 2019-11-12 17 NULL 207
The Store
and Hub
tables will include location info along with some store and hub specific fields.
The issue is how to handle creating a good alternate key for the Sale
table (SaleID
is my surrogate primary key.) In theory a sale should be determined by date and location. However I have two location fields and they are both nullable. I'm wondering if I can handle this using unique indexes? Or can I use check constraints somehow? Or an intermediate location table?
Also, as a follow up, suppose I also had a SuperHub
table (imagine Hub
is to SuperHub
what Store
is to Hub
.) If I wanted to add a SuperHubID
field to my Sale
table, how would this affect things? With 3+ nullable fields don't check constraints/unique indexes start getting out of hand?
Here is potentially what an ERD for this could look like:
Edit: To clarify, I'm trying to figure out the best way to include location info as part of the alternate key for the Sale
table, given that "location info" is split among several different tables (the primary key for the Sale
table is just a surrogate key SaleID
.) Trying to include all the location tables in the alternate key leads to Nulls appearing in the key (see sample from Sale
table above.) What is the best way to deal with this issue?
Edit 2: Ok, here is some more clarification. I'm including table definitions, etc.
--Table Defs
CREATE TABLE Sale (
SaleID int NOT NULL IDENTITY(1, 1),
SaleDate date NOT NULL,
StoreID int NULL,
HubID int NULL,
Quantity int NOT NULL);
-- StoreID and HubID are nullable since a sale can occur at one or the other
-- Is there a better way to organize this?
-- @PeterHe mentioned Category/Subcategory model
CREATE TABLE Store (
StoreID int NOT NULL IDENTITY(1, 1),
StoreNumber int NOT NULL,
Customer nvarchar(20) NOT NULL,
Address nvarchar(50) NOT NULL,
HubID int NOT NULL);
CREATE TABLE Hub (
HubID int NOT NULL IDENTITY(1, 1),
HubNumber int NOT NULL,
Customer nvarchar(20) NOT NULL,
Address nvarchar(50) NOT NULL);
--PKs
ALTER TABLE Sale
ADD CONSTRAINT PK_Sale PRIMARY KEY CLUSTERED (SaleID);
ALTER TABLE Store
ADD CONSTRAINT PK_Store PRIMARY KEY CLUSTERED (StoreID);
ALTER TABLE Hub
ADD CONSTRAINT PK_Hub PRIMARY KEY CLUSTERED (HubID);
--FKs
ALTER TABLE Sale
ADD CONSTRAINT FK_Sale_Store
FOREIGN KEY (StoreID) REFERENCES Store (StoreID)
ON UPDATE NO ACTION
ON DELETE NO ACTION;
ALTER TABLE Sale
ADD CONSTRAINT FK_Sale_Hub
FOREIGN KEY (HubID) REFERENCES Hub (HubID)
ON UPDATE NO ACTION
ON DELETE NO ACTION;
ALTER TABLE Store
ADD CONSTRAINT FK_Store_Hub
FOREIGN KEY (HubID) REFERENCES Hub (HubID)
ON UPDATE NO ACTION
ON DELETE NO ACTION;
--AKs
ALTER TABLE Store
ADD CONSTRAINT AK_Store UNIQUE (StoreNumber, Customer);
ALTER TABLE Hub
ADD CONSTRAINT AK_Hub UNIQUE (HubNumber, Customer);
ALTER TABLE Sale
ADD CONSTRAINT AK_Sale UNIQUE (SaleDate, StoreID, HubID);
--issue here is StoreID and HubID are nullable since sale could occur at either