Oracle SQL: Foreign key from three possible tables? - sql

Take the following scenario:
CREATE TABLE customers (
cust_num INTEGER PRIMARY KEY,
cust_name VARCHAR(60) NOT NULL,
//other info
);
CREATE TABLE checking_account (
acc_num NUMBER(16) NOT NULL,
acc_type VARCHAR(8) NOT NULL,
//other info
);
CREATE TABLE savings_account (
acc_num NUMBER(16) NOT NULL,
acc_type VARCHAR(8) NOT NULL,
//other info
);
CREATE TABLE loan_account (
acc_num NUMBER(16) NOT NULL,
acc_type VARCHAR(8) NOT NULL,
//other info
);
CREATE TABLE has_account (
acc_num NUMBER(16) NOT NULL,
acc_type VARCHAR(8) NOT NULL,
cust_num INTEGER
);
More than one customer may have the same account and additionally, one customer may have multiple accounts. The account number in the has_account table may or may not be unique across accounts.
How could this be represented? I have tried to implement class table inheritance and concrete inheritance but I can't figure out how to allow one account number to be shared across multiple accounts. I have yet to find an example or explanation which makes this consideration. Could anybody give me an insight as to how to achieve this functionality or at least point me in the right direction? Any help is greatly appreciated

'customers' table is your primary table which should be linked with all 3 tables 'checking_account','savings_account' and 'loan_account'.In these 3 table there should be one column cust_num which will represent forign key.
So if customer has saving account and loan account then for this customer there is 2 row in customers table and one-one row in savings_account & loan_account table.
Customer all account info should be in has_account table where cust_num is forign key so you can easily find customer info with his account details via join on customer & has_account table.
If you want to know one customer has how many account then use count(cust_num) in your customers table.
Note - If you follow good DB design then you should have only one table called as 'cust_account' in which columns should be like acc_num,acc_code,acc_name etc and acc_type column should be updated with valid value like 'saving','loan' or 'checking'.
In your table structure acc_type column is given for all 3 account type tables which has no sense if you have different table for different account type.Remove this column if you are going to use seprate table for account type otherwise use one table with column acc_type.

Not a complete answer and too long for a comment but I thought I'd address some of your reasons why you have three separate tables:
"checking account doesn't have an interest rate"
This is a business rule and should not be implemented by a different table structure. Also, in times of higher interest rates it's certainly plausible for a checking account to earn interest. Business rules are usually much easier to change that data structures.
a loan account doesn't have a balance
Again, this is a business rule - but certainly a loan has a principle balance.
one account number may be shared across multiple account types ... account number would need to be a primary key in which case it couldn't be shared across accounts
One way to solve that is to use account number , account type as a "logical" compound primary key (note that in most DB systems there are benefits to using a sequence number as a "true" primary key that is independent of the actual record information. What if an account number changes for some reason?
If there are attributes of one account type that cannot feasibly stored in a "shared" data model, then you could model those as sub-tables:
|- 0:1 -- 0:1 CheckingAccount
Customer 1--* Account -|- 0:1 -- 0:1 SavingsAccount
|- 0:1 -- 0:1 LoanAccount
But you may find that you end up with similar problem that are more easily solved using business rules that separate data structures.

Create custReg table which have parent-child relationship of account types between column data. That column named accountID would be PK. As common attributes can easily be placed in single table.
Further tables with different attributes can be created and subsequently linked with theirs account ID at first-child level.
Then use hierarchical queries to access data between tables.

Related

What table structure to go for if there are two objects of same type but of different nature?

Given that there are two kinds of products X and Y. X has A, B and C as the primary key whereas Y has A and D as it's primary key. Should I put them in the same table ? Why should I and if I should not, then why is that ?
I have currently put them in two separate tables but some colleagues are suggesting that they belong in the same table. My question is should I consider putting them in the same table or continue with different tables?
Below I have given example tables for the above case.
CREATE TABLE `product_type_b` (
`PRODUCT_CODE` VARCHAR(50) NOT NULL,
`COMPONENT_CODE` VARCHAR(50) NOT NULL,
`GROUP_INDICATOR` VARCHAR(50) NULL DEFAULT NULL,
`RECORD_TIMESTAMP` DATE NULL DEFAULT NULL,
PRIMARY KEY (`PRODUCT_CODE`, `COMPONENT_CODE`)
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;
CREATE TABLE `product_type_a` (
`PRODUCT_CODE` VARCHAR(50) NOT NULL,
`CHOICE_OF_COVER` VARCHAR(50) NOT NULL,
`PLAN_TYPE` VARCHAR(50) NOT NULL,
`RECORD_TIMESTAMP` DATE NULL DEFAULT NULL,
`PRODUCT_TENURE` INT(11) NULL DEFAULT NULL,
PRIMARY KEY (`PRODUCT_CODE`, `CHOICE_OF_COVER`, `PLAN_TYPE`)
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;
As you can see there are certain fields that are not common to both tables but are part of the primary key. There are also some other fields which are not common to both tables.
Here is the bigger picture of the system in consideration.
Each product type has a different source from where it is sent to the system.
We need to store these products in the database.
I would like to have a balance between normalization and performance so that my read-write speeds aren't compromised much due to over normalization.
There is also a web-app which will have a page where these products are searchable by the user.
User will populate certain column fields as filters based on which we need to fetch the products and show on the UI.
variations in subtypes is currently 2 and is not expected to increase beyond 4-5 which again is go
ig to be over a decade maybe. This again is an approximation.n
I hope this presents a bigger picture of the system.
I want to have good read and write speeds without compromising much. So should I go ahead with this design ? If not, what design should be implemented ?
For a trading system and taking into account max 5 product types and very limited number of attributes I'd prefer a single table for all products with a surrogate PK. Think about references to products from trading transactions, this is the biggest part of the total DB content in a long run.
A metadata table describing every product-specific attribute and its mapping to the general table column would help to build UI and backend/frontend communications.
Search indexes would reflect most popular user seraches depending on product type.
This is typical Category/SubCategory model issue. There are a few options:
Put everything in one table, which will have some columns NULLable
because different subtypes do not have the same attributes;
One parent table for all the common attributes, and also with the
column of the type indication column. Then each sub type has its own
table just for the columns the Subtype has.
Each subtype has its own table, including all the common columns of
all the sub type.
(1) is good if the sub type is very limited;
(3) is suitable if the variations of the sub types are very limited.
The advantage of (2). is it is easy to return all the records with the common columns. And if an artificial key (like auto-increment id) is used, it ensures all records, regards less the sub type, has unique id.
In your case, no artificial PK is used, I think your choice is not bad.

How to design entity tables for entities with multiple names

I want to create a table structure to store customers and I am facing a challenge: for each customer I can have multiple names, one being the primary one and the others being the alternative names.
The initial take on the tables looks like this:
CREATE TABLE dbo.Customer (
CustomerId INT IDENTITY(1,1) NOT NULL --PK
-- other fields below )
CREATE TABLE dbo.CustomerName (
CustomerNameId INT IDENTITY(1,1) NOT NULL -- PK
,CustomerId INT -- FK to Customer
,CustomerName VARCHAR(30)
,IsPrimaryName BIT)
Though, the name of the customer is part of the Customer entity and I feel that it belongs to the Customer table.
Is there a better design for this situation?
Thank you
Personally, I would keep the Primary name in the Customer table and create an "AlternateNames" table with a zero-to-many relationship to Customer.
This is because presumably most of the time when you are returning customer data, you are only going to be interested in returning the Primary Name. And probably the main (if not only) reason you want the alternate names is for looking up customers when an alternate name has been supplied.
Unfortunately, this is too long for a comment.
Before figuring this out, more information is needed.
Is additional information needed for names? For instance, language or title or date created?
Are the names unique? Is the uniqueness within a customer or over all names?
Are the primary names unique?
Does every customer have to have a primary name?
How often does the primary name change to an alternate name? (As opposed to just having the name updated.)
When querying the data, will you know if the name is a primary or alternate name? (Or do they all need to be compared?)
Depending on the answer to this question, the appropriate data structure can have some tricky nuances. For instance, if you have a flag to identify the primary name, it can be tricky to ensure that exactly one row has this value set -- particularly when updating rows.
Note: If you update the question with the answers, I'll delete this.

Can a junction table attribute be used as foreign key in another table?

Suppose I have a table patients and a table tests. Patients can take many tests and tests are given to many patients. so it's a many to many relationship. I create a junction table between them. I also want to store the date the patient wants to take the tests so I do that in the junction table. Also, suppose a patient takes more than 1 test on a single day and I want to create a specific billing account against all the test on that date.
I create an autogenerated billing no. I add that to the junction table as well.
So far, my junction table record the tests a patient may have taken on a date and the billing no.
Now I create a table billingaccounts. In this table I want to store all the billing information against 1 billing No. such as totalamount, paid amount and so on. I also want to include the billing no here, since it will help me identify the patient and tests the billing account field refers to.
I was trying to make the billing no in this billing account table as a foreign key to the billing no in the previous junction table.
But I can't do that since the billing no in the junction table cannot be kept unique since it's an m:n junction table.
What can I do to make this work or any other alternatives?
Just add a primary key column to the "junction" table. Your junction table is richer than simply matching two tables. Because you want a foreign key relationship, it is worthy of its own primary key. For example (using MySQL syntax):
create table PatientTests (
PatientTestId int primary key auto_increment,
PatientId int not null,
TestId int not null,
TestDate date,
BillingAccount int,
. . .
constraint fk_patienttests_patientid references Patients(PatientId),
. . .
);
create table
Make the billing no in the junction table a foreign key to the primary key of the billingaccounts table. So, you would create a new row in billingacounts first, including an autogenerated id/billing no, then use that number in the junction table.

Normalizing a table with duplicate rows and many-to-many relationships

I am designing the database for an accounting system, currently working on the Expenses table.
According to IRS rules, whenever you update a row in any accounting table, you need to cancel out the existing row by negating its values, and create a new row with the modified information, like so:
Set the row's Status flag to "Modified"
Create an identical copy of this row, with all Money fields negated, so that the sum of the two rows is 0
Create a 3rd row, identical to the first one, with the modified data
Each expense has an identity field called ID for internal identification purposes, and an ExpenseID field, which identifies the transaction to the users. The two cannot be the same, because
ExpenseID can be repeated twice if the transaction was modified and its row was duplicated.
ExpenseIDs MUST be consecutive and NEVER have gaps, while identity fields can skip numbers if a transaction is rolled back and the identity is not reseeded.
In general, I believe the primary key should have no business meaning whatsoever.
My problem is that there are other tables used to link these expenses Many-To-Many to other objects in our system. E.g.: each expense can be linked to documents, folders, users, etc.
So it looks something like this:
create table Expenses (
ID int not null identity(1,1),
ExpenseID int not null,
Amount Money not null,
Status tinyint not null,
[...]
)
create table Expenses_Users (
ExpenseID int not null,
UserID int not null
)
alter table Expenses_Users add constraint FK_Expenses_Users_Expenses
foreign key (ExpenseID) references Expenses (ID)
alter table Expenses_Users add constraint FK_Expenses_Users_Users
foreign key (UserID) references Users (ID)
Now, because of the IRS guidelines, I have to duplicate not only rows in the Expenses table, but also in Expenses_Users, and any other table that links Expenses to other tables.
I have two ideas on how to solve this:
Option One: Normalize Expenses like this:
create table Expenses (
ID int not null identity(1,1),
ExpenseID int not null,
Status tinyint not null,
[...]
)
create table ExpensesNormalized (
ExpenseID int not null,
Amount Money not null
)
alter table ExpensesNormalized add constraint FK_ExpensesNormalized_Expenses
foreign key (ExpenseID) references Expenses(ExpenseID)
This means I'll only have to link external tables to Expenses, not ExpensesNormalized. Also, when updating an expense, I'll only duplicate and negate the data in ExpensesNormalized, which means I'll have far less redundant data in the Expenses table.
However, I'll have to use a JOIN clause every single time I SELECT from Expenses. I fear a performance hit because of this.
Option Two: Use the same tables I use now, but have the field Expenses_Users.ExpenseID point to the field Expenses.ExpenseID. This means that I won't have to duplicate any external objects because they'll point to ExpenseID, which may occur several times.
However, this will not be a real foreign key because SQL Server does not allow foreign keys to non-unique fields, so I'll have to implement foreign key logic in a trigger.
I'm having a hard time deciding between these two options. Any feedback would be appreciated.

SQL - NULL foreign key

Please have a look at the database design below:
create table Person (id int identity, InvoiceID int not null)
create table Invoice (id int identity, date datetime)
Currently all persons have an invoiceID i.e. the InvoiceID is not null.
I want to extend the database so that some person does not have an Invoice. The original developer hated nulls and never uses them. I want to be consistent so I am wondering if there are other patterns I can use to extend the database to meet this requirement. How can this be approached without using nulls?
Please note that the two tables above are for illustration purposes. They are not the actual tables.
NULL is a very important feature in databases and programming in general. It is significantly different from being zero or any other value. It is most commonly used to signify absence of value (though it also can mean unknown value, but that's less used as the interpretation). If some people do not have an invoice, then you should truly allow NULL, as that matches your desired Schema
A common pattern would be to store that association in a separate table.
Person: Id
Invoice: Id
Assoc: person_id, assoc_id
Then if a person doesn't have an invoice, you simply don't have a row. This approach also allows a person to have more than one invoice id which might make sense.
The only way to represent the optional relationship while avoiding nulls is to use another table, as some other answers have suggested. Then the absence of a row for a given Person indicates the person has no Invoice. You can enforce a 1:1 relationship between this table and the Person table by making person_id be the primary or unique key:
CREATE TABLE PersonInvoice (
person_id INT NOT NULL PRIMARY KEY,
invoice_id INT NOT NULL,
FOREIGN KEY (person_id) REFERENCES Person(id),
FOREIGN KEY (invoice_id) REFERENCES Invoice(id)
);
If you want to permit each person to have multiple invoices, you can declare the primary key as the pair of columns instead.
But this solution is to meet your requirement to avoid NULL. This is an artificial requirement. NULL has a legitimate place in a data model.
Some relational database theorists like Chris Date eschew NULL, explaining that the existence of NULL leads to some troubling logical anomalies in relational logic. For this camp, the absence of a row as shown above is a better way to represent missing data.
But other theorists, including E. F. Codd who wrote the seminal paper on relational theory, acknowledged the importance of a placeholder that means either "not known" or "not applicable." Codd even proposed in a 1990 book that SQL needed two placeholders, one for "missing but applicable" (i.e. unknown), and the other for "missing but inapplicable."
To me, the anomalies we see when using NULL in certain ways are like the undefined results we see in arithmetic when we divide by zero. The solution is: don't do that.
But certainly we shouldn't use any non-NULL value like 0 or '' (empty string) to represent missing data. And likewise we shouldn't use a NULL as if it were an ordinary scalar value.
I wrote more about NULL in a chapter titled "Fear of the Unknown" in my book, SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
You need to move the invoice/person relation to another table.
You end up with
create table Person (id int person_identity)
create table PersonInvoice (id int person_id, InvoiceID int not null)
create table Invoice (id int identity, date datetime)
You need this for some databases to allow in InvoiceId to be a foreign key as some do not allow NULLS in a foreign key.
If a person only can have one invoice then PersonInvoice can have a unique constraint on the person_id as well as the two columns together. You can also enforce having a single person for a invoice by adding a unique constraint to the invoiceID field.