One to many relationship with ensuring at least 1 record exists in (many table) - sql

How would you design tables in the following scenerio.
I have two tables in one-to-many relationship.
Table A - One
Table B - Many
Such relation doesn't give me
on database level the protection that at least 1 record will be present in B table.
Moreover Table A should know the last identifier from Table B (basing on any rule).
How could I accomplish such a task?

The foreign key will be in Table B which will guarantee that every row will have a corresponding row in Table A. In a one-to-one relationship, you could have a redundant FK in Table A to guarantee the reverse, but for a one-to-many, that's not possible.
I came across a similar requirement a few years ago when I designed a method for maintaining versions of data. Table A would be the static or unchanging data (or just data that may change but was not necessary to track) and Table B contained each version of the data as it changed. My solution was to force all DML access to the tables through a view. Actually there were two main views, one which performed a one-to-many join which provided a complete history of the data changes. This had a "do nothing" trigger on it to render it read only (one shouldn't be able to change history). The other was a one-to-one join of the static data and only the current version. This provided the data as it existed "now." All DML went through this view.
When a row was inserted, the trigger inserted into both tables their prospective fields. When a row was updated, the static fields (if changed) was updated and the versioned data was inserted as a new row. Deletions were handled as a soft delete.
The point is, there was no way to insert only to the static table. Even if all the versioned fields of a new row happened to contained NULLs, those fields were still inserted into the versioned table. So it was not possible to have a row in Table A (my static table) that did not have at least one corresponding row in Table B (my versioned table).

Related

Multi table auditing stucture

In this scenario I have the following tables:
HEADER_TABLE
DETAIL_TABLE_1 (FK to HEADER_TABLE)
And
HEADER_TABLE_AUDIT
DETAIL_AUDIT_TABLE_1
What I would like to do is create a single snap shot of the two tables in the audit tables when a change...um "session".. occurs. So for instance, If the header record AND 3 of 20 associated child records are updated at the same time, or "session" then the triggers on each table will only result in writing of one header audit record and 20 detail audit records (as they were before changes are applied).
I had the idea to attach a change session id that I retrieve from a sequence, and attach to all the changes being made within that session (1 header record, and 3 child records) and passed on to the audit tables. This would result in 4 trigger fires between the 2 tables, but should only write 1 set of data (header record and associated detail records). A check for the change session id in the audit tables (either header or detail) would determine whether a new set needs to be created, or just skip if it already exists (1st trigger fires, doesn't find change session id, creates audit records for all tables, next trigger fires and sees same change session id exists already so it skips adding audit records and so on).
This works ok if I am jusy updating the header record only. The trouble I am running into is when I am updating child records, how do I select the 20 detail records within the trigger on the detail table (understandably, oracle doesn't allow this)?
Of course, I am open to other ideas on doing this, as this was the best I could think of to create a snap shot of data from all involved tables prior to it being updated. I have wrestled with this one for a while so any insight would be greatly appreciated.
There is a project I'm working on with a similar requirement. Let me describe how I have done this, it could be helpful for you.
Requirement: Have a copy of the entire record whenever there is a change. Suppose our table is the EMP table from the sample data provided by oracle.
Implementation:
EMP is no longer a table, it is a view. All the data (the active version AND all the older rows are stored in a table called EMP_ALL.
EMP_ALL has a couple of additional columns:
version_no: this is the timestamp in format YYYYMMDDHH24MISS so it is visible when the change was done.
active_version: contains 1 for the row which is current and 0 for all the audited rows.
emp_key: an id which is the same for each row for an employee - more about this later
EMP is a view with the definition:
CREATE OR REPLACE view EMP AS
SELECT
<all_columns>
FROM EMP_ALL
WHERE active_version = 1
There is an INSTEAD OF trigger on EMP so whenever EMP is updated, the old row is copied into EMP_ALL (with active_version=0) and the current row is updated EMP_ALL (with active_version = 1). The reason that we update the current row is that we want to keep the primary key of the active row because that can be used for any foreign key definitions. The foreign key needs to map to the active row, not to an archived row. The column EMP_KEY is used as an identifier for a single employee.
EMP_ALL has a BEFORE INSERT trigger to populate the audit columns.
The same logic can be applied to any child tables. The column "version_no" will indicate when something changed and determines the order of the changes. This is very useful when constructing an audit trail.
Note: You can not use the RETURNING INTO clause that is used by applications (like Apex) when doing insert on the EMP view.
You can get the a "session number" from the following:
select sid, serial# from v$session.
Use these along with current timestamp and the PK of the row being updated to generate a unique(? see not handled below) key for the AUDIT record. Write to the audit tables only the row being updated (update 3 of 20 details write only those 3). You can then reconstruct an "as of" version of the entire structure when ever necessary. This handles the following:
Header updated, but no details updated.
Detail(s) updated but not the header.
Multiple headers updated in single statement.
Multiple details across multiple headers updated in single statement.
What is not handled:
Multiple updates of single row within single transaction (may need
sequence to avoid duplicate generated key and provide proper sequencing).
Deletes. Especially troublesome delete header.
Inserts (if needed).

Connecting one foreign key to multiple tables (primary keys)

I am developing an application for making quotations. First you make cost break down (or calculation) and upon that result you add item to quotation. The problem is that i have many product, so each category of a product will have its own cost break down form with different parameters to be filled in. If I will have only one table for cost breakdown, then it will be huge (a lot of fields in table). I have a feeling that this is not the right approach. So I came up with diagram below:
Is this solution even possible, or I must have "N" (if I have N-tables) different FK for each cost break down table? Do you have any better solutions?
I have another question if my linking table "Quotation_QtnDetail" is necessary?
It would be possible to store a reference to a particular value in one of these tables by having a CalculationType column indicating which table the record is in, along with a generic reference ID column (containing the ID of the relevant record). For example, if you were storing a CalcId of 123 and a CalculationType of 2, this would point to the record with ID 123 in the Calc2 table.
The downside to doing this is you're going to lose the ability to validate your data using FK constraints, and it will also make joins to your calculation tables a bit more complicated.
Regarding the Quotation_QtnDetail table, unless a QtnDetail record could ever be linked to multiple Quotation records, there is no need for this extra linking table. Instead, just link it directly by adding a QtnId column to the QtnDetail table. Similarly, you may also be able to remove the Calc_QtnItm table if an item is only ever linked to a single calculation record.

Update and delete records in the fact table

I have a fact table with five dimension tables associated to it.Typically, the fact table contains the surrogate keys of each dimension and has no business/surrogate key. I am trying to load the fact table with data resulted of the staging fact table i.e.Insert new records. However, I notice the fact table can also handle other operations such as Update or Delete on data. A conditional split was used in the SSIS Package for this purpose to check if all surrogate keys are 0 then make the new insert. My question is, Can I use the surrogate keys in terms of Update or Delete?
I made an insert on the fact table just to give an idea of how the data will look like.
The answer is yes, you can. BUT, will there be a situation where one employee sold the same product, from the same supplier, to the same customer, on the same day? Perhaps a different order on the same day? (this is based on the data you present in the question)
If all the surrogate keys together can uniquely identify a record, update fact records to your hearts content. But, if that is not the case, you could end up updating records when you do not intend to update.
I tend to include an order number in the fact tables I design to help avoid that situation, but you may not have that in your actual fact tables. Including the order number is a pattern referred to a degenerate dimension in the fact table. I have found it to be pretty handy.
Anyway, the answer is the same. You can update fact records based on surrogate keys, as long as all of them together can uniquely identify the row(s) you want to update.
Don't throw caution to the wind, be sure your data warehouse is designed such that you can do this if you need to. Being able to do in place updates of facts can be nice, versus delete and replace, in that there could be fewer steps in the ETL process.

Many-to-Many but sourced from multiple tables

I am supposed to be shipping out a box with variable contents and tracking this in a database. All of my items (the contents of a box) are different types and require different tables to track their respective pieces of information, although each item type has the same length serial number (i.e. PK are the same datatype). And I have a Boxes table.
So each item has a table (~7 tables) plus the box table. I want to create a BoxContents table. I tried to make a many-to-many relationship intermediate table with two columns: one for BoxID and one for ItemBarcode, where BoxID is a FK to the PK on the Boxes table and the ItemBarcode is a FK to each of the PKs on the Items tables (i.e. I tried to link multiple tables to the same column). Unsurprisingly this didn't work. I tried to insert an item and the FK constraint was violated on all but one of the ItemBarcode relationships.
How can I construct my relationships to link several types of items to one box in one table? Is this a logical approach? Do you need more information?
You need a category hierarchy (aka. class hierarchy, subtype hierarchy, inheritance hierarchy...):
There are 3 main strategies for implementing a category hierarchy. If you choose "all classes in one table" or "class per table", then no matter how many kinds of items you have, you only need one "link" table to implement the many-to-many relationship.
My first choice, if the ItemBarcode values are truly unique, would be to:
EDIT: Added description of required triggers.
Add triggers to enforce the barcode uniqueness.
(An insert/update trigger on each item table needs to verify that all (newly) assigned barcodes do not appear in other item tables.)
Use a single BoxId/ItemBarcode table without a FK relation on the barcode side, but with triggers to ensure it remains valid.
(An insert/update trigger on the association table needs to verify that the barcodes exist in the item tables. A delete trigger on each item table needs to prevent, or cascade, deletion of items that are in the association table. An update trigger on the item tables needs to update and changed barcodes in the association table. This last may be integrated into the insert/update trigger in the prior bullet.)
Consider using a view of all items to access common data by ItemBarcode.
My second choice would be n BoxId/ItemBarcode tables for the n item types. Straightforward, but a bit busy. It makes adding a new item type messier than it needs to be.
I would not use a BoxId/ItemTypeId/ItemBarcode table. It denormalizes the data by associating the ItemTypeId and ItemBarcode again, it doesn't allow the use of a FK on the barcode side, and it still requires triggers to ensure integrity.
Don't be afraid of triggers. There are some problems that they can address quite effectively.
Relational databases are not good with this kind of problem. Your basic design is correct - an association table for FKs between the tables.
Your choices are:
Have multiple columns in your association table - one for for each item table
Merge the item data into one item table
I would go option 2.

Is it possible to implement an any-any relationship only using 2 tables?

I don't know whether my idea below is applicable:
I have 2 tables, namely A and B.
Each row in table A can be associated with zero or more rows of table B.
Each row in table B can also be associated with zero or more rows of table A.
Table A contains (among others) 2 columns AId (as a primary key) and BId (as a foreign key).
Table B also contains (among others) 2 columns BId (as a primary key) and AId (as a foreign key).
A cascade delete rule is also setup for each foreign key relationship in DB and model class.
It means deleting a row of A will also delete rows, associated with it, of B or deleting a row of B will delete rows, associated with it, of A.
Is it practically possible to do this scenario?
No, not if you are following normal form.
many to Many relationships are a hallmark of needing an intersection table.
More info:
So here's an example. question tagging. A tag can be on multiple questions, and a question can have multiple tags. this is a many to many realtionship. You COULD put multiple entries of tagIds in the Question entity's Tag Column. But you lose A LOT by doing this.
You will not have integrity, because it is VERY difficult to maintain whether or not a tag exists in your tag table as well as in the questions tag column.
This also violates normal form, because a single column cannot have multiple values.
You also cannot easily join on that column, since it has multiple values in it.
I'm assuming that by 'any-any' relationship you are referring to 'many-to-many'.
What you describe in your post is not a many-to-many relation. What you describe is two separate one-to-many relations.
You have a one-to-many relation from TableA to TableB via the AId column in TableB. And you have another one-to-many relation from TableB to TableA via the BId column in TableA. Having two one-to-many relationships in opposite direction is not the same thing as having a many-to-many relationship. Take Stefan's tagging example and consider three queries (QId1, QId2 and QId3) and three tags (TId1, TId2 and TId3). Try to express that all QId1, QId2 and QId3 are tagged each with all TId1, TId2 and TId3. You'll realize that you cannot, because you're trying to express 9 relations in only 6 available 'foreign key' fields. A true many-to-many relation requires up to MxN 'links' possible between two tables of size M and N, while your design allows for M+N (not surprising, since your design is M links in one of the 1-to-many relations and another N links in the other 1-to-many relation).
What you need is a join table. So you have an a and b table that each have a primary key. Then you create an ab table that only has 2 columns. Both are a foreign key. One goes to the a table and the other goes to the b table.
Google for "Database Normalization". You will find lots of examples.
It's possible, but you definitely don't want to do it that way.
You can use a comma separated string of identities in one of the tables. Looking up value from that table to the other is of course a major hassle. Looking up values from the the other table is a nightmare.
Using a cascading trigger with this method is of course out of the question. It might be possible by making an update trigger do the work, but the performance for that would be so bad that it's pointless.
To do this efficiently you absolutely need another table for the relations.