Database Schema Recommendation - database

I am having a brain-cease on a data problem that I am in need of modeling. I will do my best to outline the tables, and relationships
users (basic user information name/etc)
users.id
hospitals (basic information about hospital name/etc)
hospitals.id
pages
pages.id
user_id (page can be affiliated with a user)
hospital_id (page can be affiliated with a hospital)
Here is where the new data begins, and I am having an issue
groups (name of a group of pages)
groups.id
groups_pages (linking table)
group_id
page_id
Now here is the tricky part .. a group can be 'owned' by either a user or hospital, but those pages arent necessarily affiliated with that user/hospital .. In addition, there is another type of entity (company) that can 'own' the group
When displaying the group, I will need to know of what type (user / hospital / company) the group is and be able to get the correct affiliated data (name, address, etc)
Im drawing a blank on how to link groups to its respective owner, knowing that its respective owner can be different.

Party is a generic term for person or organization.
Keep all common fields (phone no, address..) in the Party table.
Person and Hospital should have only specific fields for the sub-type.
If the company has different set of columns from Hospital simply add it as another subtype.
If Hospital and company have same columns, rename the Hospital to more generic Organization
PartyType is the discriminator {P,H}

You'd have to use some form of discriminator. Like adding a column with "owner_type", you could then use either an enum, a vchar, or just an int to represent what type of owner the column represents.

Here is a good tutorial on how to model inheritance in a database while maintaining a reasonable normal form and referential integrity.
Condensed version for you: Create another table, owners, and let it keep a minimal set of attributes (what users and hospitals have in common, maybe a full name, address, and of course an id). Users and hospitals will have their respective id columns that will simultaneously be their primary keys and also foreign keys referencing users.id. Give users the attributes that hospital's don't have and vice versa. Now each hospital is represented by two easily joined rows, one from owners and one from hospitals.
This allows you to reference users.id from groups.owner_id.
(There is also a simpler alternative where you create just one table for users and hospitals and put NULLs to all columns that do not apply to a particular row, but that quickly gets unwieldy.)

HospitalGroups(HospitalID, GroupID)
UserGroups(UserID, GroupID)
CompanyGroups(CompanyID, GroupID)
Groups(GroupID,....)
GroupPages(GroupID, PageID)
Pages(PageID, ...)
Would be the classic way.
The discriminator idea mentioned by #Robert would also work, but you lose referential integrity, so you need more code instead of more tables.

Related

DB table many-to-many connection to children and grandchildren directly

I'm designing a database with a connection I haven't encountered before and wondering the best approach.
Let's say I have an Invoice, and that invoice can be assigned to an Organization, or an Individual, and in some cases that individual can be part of an Organization.
The way I have this thought-out so far is as follows:
Organizations Invoices Individuals
-pk -pk -pk
-name -organization_id -org_member_id
-address_id -individual_id -name
-... -... -...
So if an invoice is assigned to an individual, the individual_id is used. If that individual is associated with an organization then a through association would pick that up... (but i imagine organization_id would remain nil?) However if only an organization is assigned to the invoice then individual_id would of course be nil.
Not sure what the best way to go about this is. Thanks in advance for any advice.
There are multiple ways in which you can approach this.
One approach : as you mentioned, if the Invoice is individual based, then only individual_id is filled while keeping the organization_id as null. If that individual is part of an organization, then you can fill that organization's ID in to organization_id - so this column can be NULLABLE in your schema. If invoice is assigned to an organization only, then fill that id and keep individual_id as NULL.
Another approach : Introduce a column named assignee_type [char(1)] and use either O or I to determine the type of assignment, and just fill the assignee_id column with either Individual or Organization ID only. When you query the data, you need to refer the assignee_type column and then based on that join with either Organization table or Individual table - this can add overhead.
Both approaches have their own pros and cons, it depends on how your retrievals are going to be from this Invoice table, that will influence which approach you could take.

what is the role of categorization in database?

let me know what categorization is in database and what role of it is ...
i got it from a site, i do not understand...
Categorisation is a process of modelling of a single subtype (or subclass) with a relationship that involves more than one distinct supertype (or superclass). Till now all the relationships that have been discussed, are a single distinct supertype. However, there could be need for modelling a single supertype/subtype relationship with more than one supertype, where the supertypes represent different entity set.
thanks!
https://books.google.co.uk/books?id=9m382yDgxRsC&pg=PA287&lpg=PA287&dq=7.4.+Categorisation+Categorisation+is+a+process+of+modelling+of+a+single+subtype+(or+subclass)+with+a+relationship+that+involves+more+than+one+distinct+supertype+(or+superclass).&source=bl&ots=7JFawUEg3d&sig=peeXz5QajJFdkFHw0TzlvQFwix8&hl=ko&sa=X&ved=0ahUKEwi1u8iHwIHSAhWMVhQKHfr_AkkQ6AEIIDAA#v=onepage&q=7.4.%20Categorisation%20Categorisation%20is%20a%20process%20of%20modelling%20of%20a%20single%20subtype%20(or%20subclass)%20with%20a%20relationship%20that%20involves%20more%20than%20one%20distinct%20supertype%20(or%20superclass).&f=false
As I understand, formally categorization is a process of creating of a relation (category) that contains tupels that are a subset of the union of the tupels of the superclasses. The tupels for that subset are chosen based on certain characteristic. Consider an example:
Lets say, we have Suppliers(id, name, address, email, bank_acct, paypal, ... etc.) relation and Customers(ssn, name, faname, email, address, paypal, ... etc.) relation. So, we could create another relation featuring only those parties (both suppliers and customers) who have paypal accounts - Paypal_account_holders(id, name, address, paypal_acct, email ... etc.) where Paypal_account_holders.id is surrogate primary key for Paypal_account_holders and foreign key to both Suppliers.paypal and Customers.paypal.
Motivation and advantages:
Universal interface for applications;
Security. Restricted access to tables when you allow users to access only
some part of information;
Simplified queries;
Enforcing some business rules for certain category;
etc.
Again, that's how I understand it.

What's the proper way to associate different account types (database types) to payments and invoices?

I've run into a bit of a pickle during my development of a web application. I've boiled down the complexity of the application for sake of simplicity in this question.
The purpose of this web application is to sell insurance. Insurance can be purchased through an agent (Agency) or over the phone directly (Customer). Insurance policies can be paid through the agency or the customer can pay for the policy directly. So money is owed (invoiced) and received (payments) from multiple sources (Agencies/Customers).
Billing Options:
Agency (Agency collects from customer outside of app)
Customer
Here's where it gets complicated. Agencies are stored in a separate database table than customers (for obvious reasons). However, both agencies and customers need to be able to make payments and have invoices assigned to them. I'm having difficulty figuring out how to create the proper database schema to allow for both types of database records to be connected to their invoices and payments.
My initial plan was to set up separate relationship (joining) tables that link the agencies and customers to invoices/payments.
However, now that I've been thinking about the problem more, I think it might be beneficial to merge both agencies and customers into a single "Payee" table which would then be associated with payments/invoices. The payee table would only store a primary key. It would not contain actual names or info for the payee - instead I would pull that data via a JOIN with either the agencies or customers tables.
Regardless of whatever solution I choose I am still faced with the problem when creating a new payment record is that I need to scan both the agencies and customers table for possible payees. I'm wondering if there's a proper way to approach this from a database schema standpoint (or from an accounting/e-commerce standpoint).
What is the correct way to handle this type of situation? All ideas and possible solutions are most welcome!
Update 01:
After a few helpful suggestions (see below) I've come up with a possible solution that may solve this issue while keeping the data normalized.
The one thing about this method that rubs me the wrong way is that I will have to make multiple table selects to get a list of all the people who can potentially make payments and/or have invoices assigned to them.
Perhaps this is unavoidable though in this situation since indeed there are different "types" of people that can be associated with payments and invoices. I'm stuck with a situation where I have two different types of records that need to be associated to the same thing. In the above approach I'm using the FKs to link each table (Agencies/Customers) to a Payee record (the table that unifies both Agencies/Customers) and then ultimately links them to Payments and Invoices.
Is this the proper solution? Or is there something I've overlooked?
There are several options:
You might put this like you'd do it with OOP programming and inheritance.
There is one table Person which holds an uniqueID and a type (Agency, Customer, more in Future). Additionally you might add columns with meta-data like who inserted/when/why and columns for status/soft-delete/???
There are two tables Agency and Customer, both holding a PersonID as FK.
Your Payee is the Person
You might use a schema-bound VIEW with a UNION ALL to return both tables of your modell in one result. A unique index on this view should ensure, that you'll have a unique key, at least as combination of the table-source and the ID there.
You might use a middle table with the table-source and the ID there as unique Key and use this two-column-id in you payment process
For sure there are several more...
My best friend was the first option...
My suggestion would be: instead of Payees table - to have two linking tables:
PayeeInvoices {
Id, --PK
PayeeId,
PayeeType,
InvoiceId --FK to Invoices tabse
}
and
PayeePayments {
Id, --PK
PayeeId,
PayeeType,
PaymentId --FK to Payments table.
}.
PayeeType is an option of two: Customer or Agency. When creating a new payment record you can query PayeeInvoices by InvoiceId to get PayeeType and corresponding PayeeId, and then lookup the rest of the data in corresponding tables.
EDIT:
Having second thoughts now. Instead of two extra tables PayeeInvoices and PayeePayments, you can just have PayeeId and PayeeType columns right in Invocies and Payments tables, assuming that Invoice or Payment belongs only to one Payee (Customer or Agency). Both my solutions are not really normalized, though.

Which is the best way to relate some tables?

I want to make an application where there will be different users and each user will have a set of friends which will be put in categories. There will be some default categories, but the user will be able to add his own. I was wondering which will be the best way to do this.
My idea is to have 3 tables - user, friends and categories.
The user table to have fields (one to many) for friends and categories (but I don't know if the user table will need any information about the friends and the categories at all).
The friends table to have a field for categories (one to many) and a field for the user (many to one).
The category table to have fields for user (many to many?) and friends (many to many?).
I'm not sure about the relations, too. I'm using PHP with MySQL and Symfony2 and Doctrine2. Please help!
EDIT
Maybe I haven't described exactly what I need. When you open the app, you see a login form. If you don't have an account, you should register - the registration creates a new user. This user isn't connected with other users (I'm still new to programming and I want something a little easier so it's something like phonebook). Each user has a list of friends and a firend is a row in a table with fields such as name, addres, phone, email, photo, birthday and so on, but they are added by the current user. The friends are not users. Every user is in fact an account with password and username and when you log in there is just a list of friends. So each user creates categories for himself and he has nothing to do with other users and their categories. The category will have only id and name.
So the idea is that you create an account, then create some categories and add friends to them just to have an organiser when you friends are born or where they live, or which is their phone number, but you create them and add the information about them, they are to users themselves. It's not like a social network. Just a notebook where each user can write info about his friends.
First of all, you need to understand the role of intersection tables: if user A labels user B as a friend (i.e. there is a many-to-many relation from user to itself), and you create a new table to represent that relation (the friends table), any additional information about this "friendship" should be linked to that table. So, if a user categorizes his friends in some way, the category applies to friends, not to user. There's no need for a relation between category and user for this specific purpose.
Update: since friends are not users, the friends table will not be an intersection table (and thus have only one reference back to user, denoting the "owner"), but the rest of the answer still applies.
I'm assuming each category will be a row in the category table. Additional information about the category might be added, but it should be limited to that. For instance, if you want to know which user created a category, you could add a foreign key to user labeled for instance "owner" or "created_by". That might be useful if categories created by one user are not to be seen by others.
Finally, you can relate friends with category. If User A can put user B in at most one category, then a foreign key from friends to category should suffice (i.e. a one to many relation). Otherwise, you might need another many-to-many relation, so an additional intersection table should be created (for instance friend_category).
You could avoid this extra table by employing denormalization, having multiple rows in friends where both users are the same (and in the same order) but the category is different (see also this example). Whether this is advantageous or not is beyond the scope of this answer, but IMHO using an extra table is better for now (it might seem more complicated, but it will be easier to maintain in the long run). (Update: if friends is not an intersection table, denormalizing like this is not really an option, so stick with the friend_category table)
In the end, your layout would look like this:
user friends friend_category category
---- ------- --------------- --------
(user fields) <-- user (owner) <-- friend (category fields)
(friend fields) category --> user (owner) --+
^ |
| |
+--------------------------------------------------------------------+
I can suggest the following table set for this (this scheme applies to the phonebook or social network tasks as well):
Table "Users" that stores all the information about users:
UserId
Name
Phone
Address
... (any other fields)
Table "Categories" that stores information about relationship categories:
CategoryId
Name
Table "Relationships" that stores information about relationships between users:
FirstUserId -> Link to Users table
SecondUserId -> Link to Users table
CategoryId -> Link to Categories table
So, any user is able to add new categories, and then reference them when adding new relationship to another person.
If you need to select all user's friends, you will have to:
select fr.* from Relationships r join Users fr on r.SecondUserId = fr.UserId where r.FirstUserId = <Current user id>

How to model this one-to-one relation?

I have several entities which respresent different types of users who need to be able to log in to a particular system. Additionally, they have different types of information associated with them.
For example: a "general user", which has an e-mail address and "admin user", which has a workstation number (note that this a hypothetical case). Both entities also share common properties like first name, surname, address and telephone number. Finally, they naturally need to have a (unique) user name and a password to log in.
In the application, the user just has to fill in his user name and password, and the functionality of the application changes slightly according to the type of the user. You can imagine that the username needs to be unique for this work.
How should I model this effectively?
I can't just create two tables, because then I can't force a unique constaint on the user name.
I also can't put them all in just one table, because they have different types of specific information associated to them.
I think I might need 3 seperate tables, one for "users" (with user name and password), one for the "general users" and another one for the "admin users", but how would the relations between these work? Or is there another solution?
(By the way, the target DBMS is MySQL, so I don't think generalization is supported in the database system itself).
Your 3 tables approach seems Ok.
In users table have only ID, username, password,usertype.
In general users table have ID, UserID (from users table), other fields.
Same thing for admin users.
Usertype field will tell you from what table to search for additional info
if(usertype==admin)
select * from admins where userid=:id;
else
select * from general where userid=:id;
Two tables. USERS with user names, first, last, etc. ROLES with roles, and a link back to the user name (or user id or whatever). Put a unique constraint on the user name. Put workstation nbr, email, phone, whatever else you need, in the user table. Put 2 columns in the ROLES table -- USERID and ROLE.
You should decide how much specific information is being stored (or likely to be stored in the future) and make the decision based on that. If there are only a handful of fields for each user type then using a single table is alright.
USERS table (name, type, email, password, genfield1, genfield2, adminfield1, adminfield2)
Make sure to include the type (don't assume because some of the fields particular to that user are filled in that the user is of that type) field. Any queries will just need to include the "AND usertype = " clause.
If there are many fields or rules associated with each type then your idea of three tables is the best.
USERS table (ID, type, name, password)
GENUSERS (ID, genfield1, genfield2)
ADMINUSERS(ID, adminfield1, adminfield2)
The constraints between IDs on the table are all you need (and the main USERS table keeps the IDs unique). Works very well in most situations but reports that include both types of users with their specific fields have to be done in two parts (unioned SQL or subqueries or multiple left joins).
You can solve it with one 'general' users table containing the information thats available for all users and 1 table for every specific user type. In your example you will then need 3 tables.
Users: This table holds only information shared between all usertypes, ie. UserId, Name, Address, etc.
GeneralUsers: This table 'extends' the Users table by providing a foreing key UserId that references the Users table. In addition, information specific to general users are held here, fx. EmailAddress, etc.
AdminUsers: As with GeneralUsers, this table also 'extends' the Users table by providing a foreign key UserId referencing the Users table. In addition information specific to admin users are held here, fx. WorkstationId, etc.
With this approach you can add additional 'specializations' if the need arises by simply adding new tables that 'extends' the Users table using a foreign key reference. You can also create several levels of specialization. If for example admin users are general users as well as admin users then AdminUsers could 'extend' GeneralUsers instead of Users simply by using a foreing key to GeneralUsers instead of Users.
When you need to retreive data from this model you need to which type of user to query. If for example you need to query a GeneralUser you will need something similar to:
SELECT * FROM GeneralUsers
LEFT JOIN Users ON GeneralUsers.UserId = Users.UserId
Or if querying an admin user
SELECT * FROM AdminUsers
LEFT JOIN Users ON AdminUsers.UserId = Users.UserId
If you have additional levels of specialization, for example by having admin users also being general users you just join your way back.
SELECT * FROM AdminUsers
LEFT JOIN GeneralUsers ON AdminUsers.UserId = GeneralUsers.UserId
LEFT JOIN Users ON GeneralUsers.UsersId = Users.UserId
I most definitely would not do a model where you have separate tables as in GeneralUser, AdminUser and ReadOnlyUser.
In database design, a good rule of thumb is "Down beats across". Instead of multiple tables (one for each type), I would create a SystemUsers table, and a Roles table and define a join table to put SystemUsers in Roles. Also, I would define individual roles.
This way, a user can be added to and removed from multiple roles.
A role can have multiple permissions, which can be modified at any time.
Joins to other places do not need a GeneralUserId, AdminUserId and ReadOnlyUserId column - just a SystemUserId column.
This is very similar to the ASP.Net role based security model.
alt text http://img52.imageshack.us/img52/2861/rolebasedsecurity.jpg

Resources