Entity Relationships - Can a weak entity take part in a 'one to many' relationship as the 'one' - primary-key

With the following entity relationship structure I'm struggling to figure out if the relationship between the LOAN and ITEM entities is valid?
The weak entity of LOAN uses a partial key of 'loan_dateLeant' and the primary keys from CUSTOMER and ITEM to form LOANs primary key.
However LOAN has a 'one to many' relationship with ITEM as a loan can consist of more than one item.
But surely this means that if more than one item is loaned, then the loan record will have two item_id values for part of its primary key?

You're right, that's not valid - you can't use a to-many relationship as a defining part of a primary key. What you might consider doing instead is adding a unique loan_id that you make part of your primary key, instead of the items the loan contains; then a single loan is defined by its customer and loan ID (or customer, date, and loan ID).
If that doesn't work, make date_leant a datetime field, and increase the precision on it until you can't possibly (within the constraints of your system) have two loans occur at the same time - how likely is it that a single customer transacts two separate loans within milliseconds of each other?
Note this doesn't prevent the LOAN entity from taking part in the relationship as the "one"; it just means that you can't define the weak entity using the "many".

Actually, you can do this.
Remember, the primary key of LOAN will only depend on the date+customer identifier. As long as there is a maximum cardinality of "1" on the "can make" relationship (i.e., a loan is related to only 1-customer), you're fine on the primary key with LOAN.
The ITEM table, will instead take the (single) identifier of the loan (customerID + date) as a foreign key within ITEM. The Primary Key of LOAN will not be affected by this modeling.

Related

Adding a constraint on foreign key

I have a superclass Promotion and it has 3 subclasses GiftCard, Offers, TodaysDeals.
Promotion(PromotionID,expiryDate)
GiftCard(GiftCardID,points)
where GiftCard.giftcardID references promotion
TodaysDeals(TodaysDealsID,Discount)
where TodaysDeals.TodaysDealsID references promotion
Offers(OffersID,Discount)
where offers.offersID references promotion
GiftCard promotions are related to customers while Offers & Deals are related to the product. A product can't have an offer and a deal at the same time.
I want to add the promotionID into the table of Products, ensuring that any promotionID corresponding to the GiftCardID isn't in the table. Is that approach possible to be represented in the eerd and translated to sql code?
I thought of doing a relation between deals and product and another between offer and product, but that won't satisfy the constraint that a product can't have an offer and a deal at the same time, because both ID's will be represented as foreign keys in the Product Table.
You can add PromoType to your tables, make Promotion(PromotionID,PromoType) UNIQUE and reference this unique key from the other tables.
Promotion(PromotionID,
PromoType check ('Deal','Offer', 'Gift'),
expiryDate,
UNIQUE(PromotionID,PromoType)
)
Product(
PromoId,
PromoType check ('Deal','Offer'),
FK (PromoId,PromoType) ref Promotion(PromotionID,PromoType)
)

I can't find any primary key in some of my relations

Alright so I read from somewhere
Every table should have a primary key
But some of my tables don't seem to behave!
I'd also like to know whether the relations as I'm using are fine or I need to dissolve them further, I'm open to suggestions.
The relations are
Dealers(DealerId(PK),DealerName)
Order(DealerId(FK),OrderDate,TotalBill)
Sales(DealerId(FK),ItemType,OrderDate,Quantity,Price)
P.S. I can't make a table named Items(ItemCode,Type,Price) Because the price is variable for different dealers. And all the constraints i.e not null + check that I needed are dealt with already just didn't mention.
1. Are the relations dissolved well?
2. Should I care about setting primary keys in the tables that don't have it already?
Helpful responses appreciated.
In your case, you should add an auto increment integer field to Order and Sales and set that to be the primary key.
In Relational Database Theory, you can sometimes identify a sub-set of the fields to use as a primary key, as long as those columns are non-null and unique. However, (1) the order table cannot have a primary key from DealerID and OrderDate because a dealer could make two orders on the same date. Maybe even for the same amount, which would mean that no sub-set of fields is unique, and (2) even when familiar data can uniquely identify the data, an auto-increment integer can be a good key.
I also think that you want a foreign key from Sales to Order. You are probably using DealerId and OrderDate for joins, but this will not work correctly if a dealer makes two orders on the same date.
Finally, take advice like
Every table should have a primary key
with a grain of salt. Linking tables used for many-to-many relationships can work perfectly fine without a primary key, although a primary key can be an improvement, since it will make deleting records easier, and if you don't have a primary key on a linking table, I would still recommend a unique index on all the fields, in which case that can be the primary key.
Do you really need separate Sales Table ?
Dealers(DealerId(PK),DealerName)
Order(OrderId(PK), DealerId(FK),OrderDate, ItemType, Quantity,Price)
Also,
TotalBill (can be calculated) = Quantity * Price
About the question 1 you should answer this question:
A sale can be made without an order?
If yes, your DealerId(FK) in Sales is alright, assuming that a sale will only exist if a dealer made it.
If no, you should put an OrderId(FK) in Sales, instead of DealerId(FK). If a sale belongs to an order, this order belongs do a dealer, so you already have the relation from dealers to sales.
About the question 2, you should have primary keys on your tables, because this is the way you have to select, update and delete some specific item on your database. Remembering that a primary key is not always an auto increment column.
And about the Items table, if the price is variable to different dealers, so you have an M to N relationship between Dealers and Items, which means you could have an intermediate table like this example:
DealerItemPrices(DealerId(FK), ItemId(FK), Price)
And these two Foreign Keys should be Unique Composite Keys, in this way a Dealer Y can't have two distinct prices to the same item.
Hope it helps!

A table with all attributes making the primary key

I have two tables: Student and Shop and I would like to record information about which student visited which shop and if the number of visits is above n, they should received a discount:
This is how I did it:
All of attributes (studentID, shopID, time, date) in table StudentShop makes the primary key for this table. I just wanted to know if this design is good?
As relation between entieties Student - Shop is many to many relationship,
it is always implemented using associative table (consists of primary keys from both relations), so StudentShop table - is good choice for implementation of such relation.

SQL One-to-One Relationship Definition

I'm designing a database and I'm not sure how to define one of the relationships. Here's the situation:
An invoice is created
If the product is not in stock then it needs to be manufactured and so a work order is created.
The relationship is one-to-one. However work orders are sometimes created for other purposes so the WorkOrder table will also be linked to other tables in a similar one-to-one relationship. Also, some Invoices won't have a work order at all. This means I can't define these relationships in the normal way by using the same primary key in both tables. Instead of doing this I've created a linking table and then set unique indexes on both fields to define the one-to-one relationship (see image).
(source: markevans.org)
.
Is this the best way?
Cheers
Mark
EDIT: I just realised that this design will allow a single work order to be linked to an invoice and also to one of the other tables I mentioned via 2 linking tables. I guess no solution is perfect.
Okay, this answer is SQL Server specific, but should be adaptable to other RDBMSs, with a little work. So far as I see, we have the following constraints:
An invoice may be associated with 0 or 1 Work Orders
A Work Order must be associated with an invoice or an ABC or a DEF
I'd design the WorkOrder table as follows:
CREATE TABLE WorkOrder (
WorkOrderID int IDENTITY(1,1) not null,
/* Other Columns */
InvoiceID int null,
ABCID int null,
DEFID int null,
/* Etc for other possible links */
constraint PK_WorkOrder PRIMARY KEY (WorkOrderID),
constraint FK_WorkOrder_Invoices FOREIGN KEY (InvoiceID) references Invoice (InvoiceID),
constraint FK_WorkOrder_ABC FOREIGN KEY (ABCID) references ABC (ABCID),
/* Etc for other FKs */
constraint CK_WorkOrders_SingleFK CHECK (
CASE WHEN InvoiceID is null THEN 0 ELSE 1 END +
CASE WHEN ABCID is null THEN 0 ELSE 1 END +
CASE WHEN DEFID is null THEN 0 ELSE 1 END
/* + other FK columns */
= 1
)
)
So, basically, this table is constrained to only FK to one other table, no matter how many PKs are defined. If necessary, a computed column could tell you the "Type" of item that this is linked to, based on which FK column is non-null, or the type and a single int column could be real columns, and InvoiceID, ABCID, etc could be computed columns.
The final thing to ensure is that an invoice only has 0 or 1 Work Orders. If your RDMBS ignores nulls in unique constraints, this is as simple as applying such a constraint to each FK column. For SQL Server, you need to use a filtered index (>=2008) or an indexed view (<=2005). I'll just show the filtered index:
CREATE UNIQUE INDEX IX_WorkItems_UniqueInvoices on
WorkItem (InvoiceID) where (InvoiceID is not null)
Another way to deal with keeping WorkOrders straight is to include a WorkOrder type column in WorkOrder (e.g. 'Invoice','ABC','DEF'), including a computed or column constrained by check constraint to contain the matching value in the link table, and introduce a second foreign key:
CREATE TABLE WorkOrder (
WorkOrderID int IDENTITY(1,1) not null,
Type varchar(10) not null,
constraint PK_WorkOrder PRIMARY KEY (WorkOrderID),
constraint UQ_WorkOrder_TypeCheck UNIQUE (WorkOrderID,Type),
constraint CK_WorkOrder_Types CHECK (Type in ('INVOICE','ABC','DEF'))
)
CREATE TABLE Invoice_WorkOrder (
InvoiceID int not null,
WorkOrderID int not null,
Type varchar(10) not null default 'INVOICE',
constraint PK_Invoice_WorkOrder PRIMARY KEY (InvoiceID),
constraint UQ_Invoice_WorkOrder_OrderIDs UNIQUE (WorkOrderID),
constraint FK_Invoice_WorkOrder_Invoice FOREIGN KEY (InvoiceID) references Invoice (InvoiceID),
constraint FK_Invoice_WorkOrder_WorkOrder FOREIGN KEY (WorkOrderID) references WorkOrder (WorkOrderID),
constraint FK_Invoice_WorkOrder_TypeCheck FOREIGN KEY (WorkOrderID,Type) references WorkOrder (WorkOrderID,Type),
constraint CK_Invoice_WorkOrder_Type CHECK (Type = 'INVOICE')
)
The only disadvantage to this model, although closer to your original proposal, is that you can have a work order that isn't actually linked to any other item (although it claims to be for an e.g INVOICE).
What you have looks to be a perfectly normal way to construct your tables.
If you think you might like to use only one link table between your WorkOrder table and whatever other tables that may have WorkOrders, you could use a link table like:
WorkOrders
OtherId (Could be InvoiceId, or an ID for SomethingElse that may have a WorkOrder)
OtherType (ENUM - something like 'Invoice', 'SomethingElse')
WorkOrderId
So the issue is that you can have invoices that don't have work orders and work orders that don't have invoices but the two need to be linked when there is a link. I would say based upon that description that your database diagram is pretty good. This would open you up to allowing more than a one-to-one relationship. This way down the road you can consider having two work orders for one invoice. You might also have one work order that handles two invoices. This opens you up to a lot of possibilities that you may not need now but that you might in the future.
I would recommend your current design. In the future, you may want to add more information about the link between invoice and work order. This middle table will allow you to add this information.
In the interest of fairness to the other side of the coin, you do need to consider speed/number of tables/etc. that this will cause. For example, you have now created a third table which increased your table count by 50% in this example. Look at the rest of your database. If you did this everywhere, you would probably have the most normalized database but it might not be the most performant because of all the joins that are necessary. Basically, this isn't a "one-size-fits-all" solution. Instead it is a design choice. Personally, I hate nullable foreign key fields. I find they don't give me the granularity I usually want with my database designs.
Your schema corresponds to a many-to-many link between the 2 tables. You are de facto opening here the possibility to have one work order for multiple invoices, and multiple work orders for one invoice. The model offers then possibilities far above the rules you are setting.
You could use a simpler schema, that will reflect the (0,1) relation between work orders and invoices, and the (0,1) relation between Invoices and Work orders:
a Work Order can be independant from
an invoice, or linked to one specific
invoice: it has a (0,1) relation to Invoice table
An invoice can have no work orders, or one work orders: it has a (0,1) relation to Work Orders Table
Such a relation can be translated by the following model and rules
Invoice
id_Invoice, Primary Key
WorkOrder
id_WorkOrder, Primary Key
id_Invoice, Foreign Key, Nulls accepted, unique value
With such a structure, it will be easy to add new 'dependants' to work orders table. If, for example, you want to open the possibility to launch work orders from restocking orders (where you want to have minimal quantities of some items in stock), you can then just add the corresponding field to the WorkOrder table:
id_RestockingOrder, ForeignKey, Nulls accepted, unique value
You'll be then able to 'see' from where your WorkOrder comes: an invoice, a restocking order, etc.
Seems it corresponds to your needs.
Edit:
as noted by #mark, SQL Server will not allow multiple null values, in contradiction with ANSI specs (check here for some more details), As we do not want to wait for SQL Server 2011 to have this rule implemented, there is a workaround here, where you can build a view excluding the null values and set a unique index on this view. I must admit that I did not like this solution ...
There is still the possibility to implement the 'unique if not null' rule in your code. It will still be simpler than implementing the many-to-many model (with the Invoice_WorkOrder table) you are proposing and manage all additional unicity rules that you'll need to implement.
There is no real need for the link table, just have them linked directly and allow for NULL in the reference field of the work order. Because a work order can be linked to multiple tables what I would do is add a reference id on every work order to every table that can link from it. So you would have:
Invoice
PK - ID
FK - WorkOrderID
SomeOtherTable
PK - ID
FK - WorkOrderID
WorkOrder
PK - ID
FK - InvoiceID (allow NULL)
FK - SomeOtherTableID (allow NULL)
To make sure a WorkOrder is linked to only one item, you have to use code to validate the row (or perhaps a stored procedure which I cannot come up with right now).
EDIT: PS, if you want to use a link table, give it a generic name and add all the linked tables with the same sort of construct I just described allowing for NULL's. In my eyes adding the extra table makes the schema larger than it needs to be, but if a work order contains a lot of big text fields it could increase performance slightly and reduce database size with all the indexes flying around. In anything but the largest applications, I would consider it over-normalization though, but that is a matter of style.

How to properly index a table two other tables have a one-to-many relationship to?

Imagine I have three tables, called "customers", "companies" and "phone_numbers". Both customers and companies can have multiple phone numbers. What would be the best way to index phone_numbers? Have both customer_id and company_id and keep one of them null? What if there are more than two tables with a one-to-many relationship with phone_numbers?
Your business rules might only state one-to-many, but in reality people & companies can be a many-to-many relationship. One person can have many phone numbers (home, cell, etc), and a phone number can relate to many people (myself, my significant other, etc). Likewise, a company number and my business number can be the same - you just use an extension number to reach me directly.
Indexing the foreign keys would be a good idea, but beware of premature optimization. Depending on setup, I'd consider a unique constraint on the phone number column but I would not have the phone number column itself as a primary key.
I would go with identity columns in the customer and company tables, then in the phone number table do as you said and keep one null and the other populated. I do something similar to this and it works out fine as long as you validate data so that it doesn't go in with both values being null. For a more elegant solution you could have two columns: one that is an id, and another that is a type identifier. Say 1 for customers and 2 for companies, that way you don't have to worry about null data or a lot of extra columns.
I'd add two columns to the phone_numbers table. The first would be an index that tells you what table to associate with (say, 1 = customers and 2 = companies). The second would be the foreign key to the appropriate table.
This way you can add as many phone number sources as you want.
If a particular person or company has more than one phone number, there would be multiple rows in the phone_numbers table.
The closest thing I have to a pattern is the following -- any two entities with a many-to-many relationship require an associative entity (a cross-reference table) between them, like so (surrogate keys assumed):
CREATE TABLE CUSTOMER_XREF_PHONE
( CUSTOMER_ID NUMBER NOT NULL,
PHONE_NUMBER_ID NUMBER NOT NULL,
CONSTRAINT CUSTOMER_XREF_PHONE_PK
PRIMARY KEY (CUSTOMER_ID, PHONE_NUMBER_ID),
CONSTRAINT CUSTOMER_XREF_PHONE_UK
UNIQUE (PHONE_NUMBER_ID, CUSTOMER_ID),
CONSTRAINT CUSTOMER_XREF_PHONE_FK01
FOREIGN KEY (CUSTOMER_ID)
REFERENCES CUSTOMER (CUSTOMER_ID) ON DELETE CASCADE,
CONSTRAINT CUSTOMER_XREF_PHONE_FK02
FOREIGN_KEY (PHONE_NUMBER_ID)
REFERENCES PHONE_NUMBERS (PHONE_NUMBER_ID) ON DELETE CASCADE
);
Such an implementation pattern can:
Be fully protected by database-level referential integrity constraints
Support bi-directional access (sometimes you need to see who else has that phone number)
Be self-cleaning if your database supports ON DELETE CASCADE
Be extended through the use of a "relationship type" attribute to map multiple independent relationships between the entities,
such as:
customer has a home telephone number
customer has a daytime telephone number
customer has a fax telephone number
customer has a mobile telephone number