Which is a good design for a database table that can be owned by two different resources, and therefore needs two different foreign keys? [closed] - database

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 11 months ago.
This post was edited and submitted for review 6 months ago and failed to reopen the post:
Original close reason(s) were not resolved
Improve this question
My application has notification settings for users that can belong to groups. A group administrator can define settings for the entire group, so that when any user performs an action, the administrator is notified. The administrator can also define settings for an individual user, which will override the group setting.
Right now I have a database with columns: group_id, action1, action2, action3, .... The actions are booleans that determine if the administrator is notified when that action is performed by a user in his or her group.
I could make a separate table owned by the User model instead of the Group model, but it feels inefficient to store the exact same data in an entirely separate table save changing the group_id to user_id.
Another option is to add user_id to the table I already have, and allow null values for group_id. When determining notification settings for a User, the application would first choose the setting based on User, and fallback to the setting where group_id is not null. This feels inefficient because there will be many null values in the database, but it definitely requires less work on my part.
Is there a design for this situation that is more efficient than the two I've described?

Generally, there are two strategies to handle a situation like this:
Use Exclusive FKs
Essentially, each of the possible parent tables will have its own, separate foreign key in the child table, and there is a CHECK enforcing exactly one of them is non-NULL. Since FKs are only enforced on non-NULL fields (meaning, when a FK is set to NULL there is no database-level validation), only one of the FKs will be enforced.
For example:
(relationship between user and group omitted)
CHECK (
(group_id IS NOT NULL AND user_id IS NULL)
OR (group_id IS NULL AND user_id IS NOT NULL)
)
Use Inheritance
Inherit user and group from a common supertype and then connect the setting to the supertype:
For more information on inheritance (aka. category, subclassing, subtype, generalization hierarchy etc.), take a look at "Subtype Relationships" chapter of ERwin Methods Guide. Unfortunately, modern DBMSes don't natively support inheritance - for some ideas about physically implementing it, take a look at this post.
This is a heavy-duty solution probably not justified for just two tables (groups and users), but can be quite "scalable" for many tables.

how about an Actions table instead?
It could have the columns:
Table Actions:
ActionId - Identity columns
Action - Store your action here; type would depend on your system
RefId - The Id for either the user or the group
RefTable - either User or Group
then when accessing the table you know your ID already, and you know if it's a group or user and can then get the appropriate action.
This make sense?
Update:
If its possible that you could have the same action for both user/group and want one to take priority (as you mentioned in your Q) you could also add a priority column and set it as a tinyInt - lower number = higher priority. Then when you select the actions you can order them by this priority. Then perform the first action, or each action in order.

Related

Is it better to use a field to nominate a Company as a Customer, or to have a related Customer table with probably only one field (FK and PK in one) [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed last year.
Improve this question
I'm redesigning our service app, and getting rid of some really awful schema problems while I'm at it. Trying to build the replacement with best practices as much as possible.
I'm having a company table rather than just customer, as it's often useful to identify companies that are not customers (suppliers, contractors, etc etc). I'm trying to decide whether it's better to simply include a boolean field represented in the relevant part of the app by a checkbox that identifies relevant companies as customers (which would become uneditable once the customer has services attached to them), or if I should, instead, have a separate table that's basically just a single field referencing the Company ID that is in turn referenced by any child records.
This similar question asks about records that can be one of several subtypes. While the question is materially different (every policy seems to be only one of the potential subtypes, whereas Companies can be any or all of Customer/Supplier/Contractor etc) its similarity combined with the fact that it has multiple conflicting answers raises the possibility that there is no industry-wide consensus, so:
Is there an established best practice here? I'm not immediately seeing any reasons that other fields should be included in the prospective Customer table, but I'm open to the idea that there might... is that a good enough reason to go with B? Or is this a clear YMMV situation, where both options have benefits, either being equally valid?
I should, instead, have a separate table that's basically just a single field referencing the Company ID that is in turn referenced by any child records.
There are probably several attributes that apply to a customer that don't apply to a non-customer Company, so CompanyID probably won't end up being the only attribute of Customer.
So if that's the case, the clear choice is to have a separate Customer table.

How to maintain two versions of same entity available for edit simultaneously with relationship? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
The problem
Consider these database tables:
Product
Order
Order Details
User
Product has columns:
Product_Name, Product_Description, Product_Size, Product_Cost, Product_Unit
Order has columns:
Order_number, Order_Total, Order_Status, Order_Payment_Status, Order_UserId (Fk of user table), Order_date
Order Details has columns:
OrderDetails_OrderId(Fk of Order table), OrderDetails_ProductId (Fk of Product table), OrderDetails_Quantity
User has columns:
User_Name, User_Phone (unique), User_Email (unique), User_Address
Consider, order statuses to be placed, packed, delivered, canceled, closed.
Now, There are three orders for the user u1:
order O1 -> Placed status (Editable by user)
order O2 -> Placed status (Editable by user)
order O3 -> Closed status (Non-editable by the user, but editable from admin)
Now the scenario is that the user u1 updates his information. This updated information should start reflecting in O1 and O2 only because they are still in the placed status; while O3 was already closed for the user and is now open only for admin's edit - so O3 should still reflect the old user information that was there previously. With the current database structure - this is not possible.
Similarly, if the admin edits product that was there in closed order, then the edits should not be displayed in the closed order.
As you may have figured out, the current structure depicted above is a simple foreign key related structure, wherein edit in one will obviously reflect directly to all related entities.
What solutions I figured out?
Solution 1: Versioning
Never update any row/entry. Always add a new row for any change (Soft updates). Keep adding rows with some tag/id/timestamp/audit trail (who edited) and map the version with the order table using mapping table.
i.e.
User_Name | User_Phone | User_Email | User_Address | Version/Timestamp
abc | 123 | abc#email.com |someaddres | v1
abc | 234 | abc#email.com |someaddress | v2
new mapping table
version | order_id
v1 | o3
Drawbacks of this solution are:
Same table multiple entries, for the same entity - then we won't be able to use unique keys. Here, phone and email are unique, but if we pick this approach then unique indices have to be removed.
All those tables (unrelated to order) that would have the foreign keys of the User table will have an impact. For example, the user_feedback table has the user's foreign key only, but now since there are multiple entries with a different version of the same user, this table will be impacted needlessly.
When the number of users would increase, performance will be impacted in select queries.
User's email is the identity, which is used for logging in. Its duplication is anyhow not possible in the same table.
No this is not audit trailing!
As per our requirement, the old information that we want to preserve for o3, should still be kept editable. Therefore, those edits shall also have to be audited. So audit trailing will be a separate wrapper altogether.
Solution 2: When order closes, create a new table with columns saving json/dump of all respective tables
i.e.
new table
order_id | JsonOfUser | JsonOfProductDetails | ...
o3 | {"name":"abc",...} | ... |
Drawbacks of this solution:
The things dumped are to be editable, but here the dumped data is difficult to edit because now the table has changed, and this table has a string/jsonb column that is effectively going to get edited, and other navigations are removed (denormalized) so all calculation changes that potentially would happen due to edits, would also have to be done manually.
Audit trailing of the edits in this table will be cumbersome because we'll be auditing the json edits here.
Deep level child jsons - increases code complexity.
Solution 3: Create copies of all tables with structure intact, that are related to order according to status events
i.e.
User_Common
User_Closed
For order O3, upon closure, all details of user_common will be copied to User_closed, and order O3 that had the foreign key of User_common table will be changed with the foreign key of User_Closed table. Now any changes in the o3 will effectively be over old data and all other open/placed orders can still get the updated information from User_common.
Drawbacks of this solution:
Suppose there happen to be 10 such tables related to order with this requirement, then each table's copies will have to be made
Each entity is now effectively represented by two tables based on event/status of the order - syncing issues and data-keeping issues may happen - i.e. maintainability.
Foreign keys are changing here for the order table. So effectively in the order table, there would be two foreign key columns: one for user_common, and other for user_closed. So when the order is open, the user_closed foreign key will remain null, and when order closes, it will get filled. Before that, 1 data operation will still occur, one to copy the information on order closure from the user_common table to user_closed.
In code, we'll always have to make a DB check for whether the lookup should be made in a common table or closed table based on order status (another DB call) - leading to code-level cognitive complexity
This was a minimal dummy replication of our requirement and proposed solutions that are thought of in research. What is the practical possible design that can adhere to this requirement without adding needless complexities?
Use the approach that has the User table add another column for a valid_since column. Apply the "never delete" strategy to Users.
If you measure performance issues add a persited/materialized (in memory) view for User that only shows the most current address - use that to get the user_id for newly placed orders and joins to show orders that are open. Joins for existing orders that use foreign keys into User mostly don't care about how many actual user_id's there are (simplified).
Use an after_insert trigger on User to propagate the new user_id to all entries of the Order to table that should reflect those changes, ignore closed orders for that update. This will be, in respect to the orders, a rather small update - how many open orders is one user allowed to have? 10? 20? 50?
Cleanup user data regularly in case they change but never order anything - those User entries can get erased.
You ensure integrity on database level that way - if you want add a report for users that change their details more then thrice a day (or limit those changes frontend wise).
Most of your user fields should also be 1:N relations - I have at least 3 telephone numbers and might use 2 addresses (living + shipping) and more then 1 email is a given.
Changing out those to own tables with a "active" might elivate the need to create full User copys. Based on buisiness needs the "address" used to ship stuff to might be worth remembering, not so much for the mobilenumber used to order or the email used to send confirmation towards but that is a business decision.
Unless you have a very active shop with millions of different users that order hundreds of orders each and change their details twenty times a month you wont get into problems using any of this with currently state of the art databse system. It seems to me this is rather a thought experiment then based in actual needs?

One-To-Many join table to avoid nullable columns [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 10 months ago.
Improve this question
I'M wondering myself whether am I the first programmer struggling with this problem, but i can't find anything in SO about this.
Point of my question, is it a good idea to make a One-To-Many join table, in order to prevent NULL references.
Let's explain, in our business requirements, we have some activities that causes a payment, i.e. sales, loans, rents, services etc. each activity can have zero or one or more payments.
When designing the DB, we have tables for each activity, Sales – Loans – Rents - Services etc, and a Payment table. The relation between the activities and the payments are one to many, each loan can have many payments, and each rent can have many payments.
But there is a problem, each payment can be a loan or a sale or any other activity, we need to relate it to its corresponding activity. I think about two options:
1) Add some Foreign keys in the Payments table for each kind of activity, LoanID - RentID - ServiceID etc. And make them Nullable, due to a loan is neither a service nor a rent.
I personally don't like this solution, it is very error prone, man can very easy forgot to add the matching FK due to it is Nullable, and then we don't know what this payment is about, we lose the Referential integrity. Although it is possible to overcome this problem by creating some constraint to ensure that there are Neither more nor less than one FK, but it is not so easy to create the right constraint and take into account all possible options, and it is hard to recreate the constraint when adding new FK columns.
Needless to say about the ugliness of such a table. Don't speak about the main issue of letting unnecessary nullable columns in a table.
2) A second solution, to create join tables in between for each kind of activity, called ActivityPayments i.e. LoanPayments etc., that holds the activity ID and the payment ID, like Many-To-Many table.
There aren’t the problems described above, each payment is related to its corresponding activity, there are no referential integrity loss, no Nullable columns.
The problem is however that it enlarges the Database, and adds another layer between the tables, and needs more work when joining in queries.
Has someone any idea?
Another option is to create a supertype table, say Activity, with all of the common attributes:
This should keep the number of tables small, and still allow you to identify the activity type for a payment. Note that this assumes that common attributes exist between the different activities. If that is not the case, the second option you listed is probably the way to go.
Look up the following tags in SO.
single-table-inheritance
class-table-inheritance
shared-primary-key
The info tab on these tags gives you a brief explanation, and the questions grouped under the tag will give you some examples.
Single table inheritance is similar you the solution you presented, and that you are unhappy with. Yes, it does involve NULLS. Generally, user errors here are prevented by the application.
Class-table-inheritance is like the solution offered by AMS. Note that SalesID and LoanID are listed as both a PK and an FK. This hints at the technique of shared primary key. With this, SalesID and LoanID are copies of a value in ActivityID. Again, it's the application layer that does the necessary work to mke sure the copies are right.
in this specific case (not necessarily applicable in similiar situations), we usualy calculate dynamically, in a view/function, each payment for what it was (in chronological order)
in other instances we had one sale table where each product can be a physical product or service or any other for-pay offer. so that limits all debit transactions to one tbale
HTA

Implementing a audit trail for our application [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed last year.
Improve this question
I want to add an audit trail for our system so when any Add/Delete/Update operation happen i will log it, with the following info:-
the CRUD operation type. is it add, delete or update.
the record ID which have been modified.
Date and time.
Now i found two approaches to follow; either to have a single audit trail table with the following fields:-
ID .such as 123445.
CRUD_description. Such as Delete
Record_ID. Such as Qaeop12771
Date. Such as 1june2O13
Or to have two tables one for a lookup table for the CRUD operation such as
CRUD_ID. such as 3.
CRUD_Description.such as Delete.
And then the Audit trial will reference the above table:-
ID. such as 123445.
CRUD_ID (this will be a foreign key to the CRUD table) such as 3.
Record_ID. Such as Qaeop12771
Date. Such as 1june2O13
So which approach is better ??
Second question If i will follow the second approach . Then is it preferred to use the CRUD_ID inside my code for example if the oprration is delete i might have my code look like:-
Inset into audit_trail (ID, CRUD_ID, Record_ID, Date) values ( 123445, 3,12771,1june2O13) //CRUID 3 represents delete operation.
Best Regards
From the viewpoint of database design (ignoring the database features and the application architecture ) I will prefer having a table for audit trail (change history) having changes per Entity and per field by implementing a flat table called Trail_History with no foreign key to any table, columns will be:
UserCode: Application user unique identifier representing who made the change. (mandatory)
TransactionCode: Any CRUD operation will need to have a unique transaction code (like GUID) (mandatory)
ChangeDate: Transaction date. (mandatory)
EntityName: Entity (table) that is being manipulated.(mandatory)
ObjectId: Entity that is being manipulated primary key.
FieldName: Entity field name.
OldValue: Entity field old value.
NewValue: Entity field new value
OperationType: CRUD operation discriminator. (mandatory)
Having this approach Any entity (table) could be traced Reports will be readableOnly changes will be logged. Transaction code will be the key point to detect changes by a single action and second question will be answered.
Hope be helpful.
Really the two approaches are prety much the same.
The seond one will be slighty more efficient but less flexible in the sense that if you need a new audit action you need to insert a new record in the type table.
The question how much information you need to audit is more interesting. Because say three users updated the record after eachother you still do not know who changed what in your design.

Database: Schema Verification [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed last year.
Improve this question
Can I please get input on the following subset of a schema?
One of the goals of this database is to be able to store the membership info for two completely different types of members. In this schema I just named them Users and Businesses. I am far enough along in the design of this database and know that Users and Businesses will come from different tables as represented here. The concern is tracking their membership information.
Here are some knowns:
Both types of members will be paying parties
Memberships can lapse and it is important to check when memberships are due
In tracking the status of a membership dates, subscription dates will need to be posted for the members to see and reminders sent out for renewal of membership
Suspended members will still exist in the DB for reactivation but will not have access until then
Each member, regardless of type, will have its own unique member id and each user/business can only have one membership
The Membership_Types table will hold information in regards to whether or not a member is a paying member or a comp member or part of any group memberships.
In the User_Memberships and Business_Memberships tables I have identified a member_status attribute as I will need a quick look into the active state of a membership. Instead of using a boolean status here should I switch it out with a membership_suspended_date and perform a calculation off of that instead?
Any input into the good or bad of this design will be greatly appreciated. Thanks
EDIT
Attempt #2 trying to take into consideration input from dportas.
Since a there can only be a given unique instance of a member (user or business) I added membership_change_date to capture the history of a member if they are to switch from free to paid to free etc.
Any inputs here still considering the original criteria listed above.
The two inline graphics do not appear in my browsers, so I am going by your text, and Ken's answer.
I do not believe this question has been dealt with fully.
Your desc of Membership_Type seems to me to be Subscription_Type
SubscriptionType holds generic info re pricing, terms, etc
Subscription holds info re the specific pricing, expiration dates, etc for a Member.
Yes, this is a classic case for Supertype-Subtypes or Orthogonal Design (commonly required but unfortunately not commonly understood)
Member is the Supertype; User and Business are Exclusive Subtypes. The Relational is 1::0-or-1 and one Subtype must exist for each Member
UserId and BusinessId are RoleNames for MemberId, implemented as Primary Keys in the Subtypes, which is also the Foreign Key to Member; there is no additional Id column in the Subtypes.
Easily implemented declaratively in SQL
This is pure Fifth Normal Form
Full Referential and Data Integrity is maintained in any Standard SQL (code in the Non-SQLs)
The Status of a Member is easily derived from the latest Subscription row MAX(Subcription.Date).
Any flag or boolean in Member for that purpose is duplicate data and will introduce an Update Anomaly (where the Normalised model has none).
▶Membership Entity Relation Diagram◀
Readers who are unfamiliar with the Standard for Modelling Relational Databases may find ▶IDEF1X Notational◀ useful.
If you provide the Group::Member info, I can model that.
"each user/business can only have one membership"
The table design you have displayed seems "over-normalized" and does not model what you are describing. The key insight is that a member of any kind is recorded only once regardless of whether they are a business or a "user", and they retain their account forever even if it lapses and gets reinstated repeatedly. This means you are only tracking one thing: users=members=businesses. That means, so far, one table.
Your second table is a transaction history for each member/user/business. Note that a comp goes in as a payment with 0.00 dollars.
"The Membership_Types table will hold information in regards to whether or not a member is a paying member or a comp member or part of any group memberships."
OK, this is the third table, membership types, with details on pricing.
You would have to tell us more about the group memberships before I can say what to do with those.
As for most of the rest of these requirements, they are all about notifications, those come out of the transaction table.
I suggest you create a new supertype table for all the data common to both types of membership (type code, status, date, duration). As a rule, I think it would be better for those columns to appear in one table, not two. In fact there's a name for this rule: The Principle of Orthogonal Design.
This pattern might also be useful to you: http://www.tdan.com/view-articles/5014

Resources