Can I combine INSERT, JOIN and ON DUPLICATE KEY UPDATE - mysql

Here's my database structure as it stands today...
inventory_transactions store movements of inventory with quantity_offset value that is either negative or positivie. They also have an inventory_transaction_id
shipments store shipments, which are groups of inventory_transactions with a shipment_id
The relationship between inventory_transactions and shipments is in a table called shipment_inventory_transactions
What I would like to be able to do is increment the quantity_offset of an inventory_transaction that is associated with a given shipment (increase the quantity of a given inventory item within the shipment) if that item already exists in the shipment.
If the item doesn't exist, create the required rows in inventory_transactions and shipment_inventory_transactions
I think some combination of JOIN and ON DUPLICATE KEY UPDATE can do this, but I can't wrap my head around it.
To simplify the situation, I'm considering removing the shipment_inventory_transactions table because the relationship between shipments and inventory_transactions is now going to be 1-to-1. The only gotcha here is that each inventory_transaction can either be associated with a shipment or a receipt, but not both. Storing them both in the same column sounds skanky. But I don't love having an extra column in every row as only one or the other will be used.
Wooh... Brain dump complete. If this made sense and you can provide a sensible answer that has eluded me, I'd be most appreciative.

Ultimately, I found simplifying the database to eliminate the many-to-many relationship let me accomplish what I wanted with a simple Insert. Better to simplify at this point than add great complexity that'll become problematic as the application grows.

Related

How to design this one to many database?

I've been doing some reading on 'one to many' databases but I'm struggling understand the best way to implement a solution in my case.
I want a MySQL database to record which employees have read certain training hand-outs at work.
So I have a table of Employees. And I have a table of Hand-outs.
I want to record if the Employee has clicked to say they've read the hand-out.
My Employee table has ID, Name, Email.
My Hand-out table has ID, Title
Am I better adding a field to the Employee table that will contain a list of "Hand-out IDs" to show which hand-outs they've read?
I need to be able to easily search to show what percentage of Employees have read a particular hand-out and I think my method would make that very difficult?
I can't have separate fields on the Employee table such as Handout1, Handout2 etc as new hand-outs will be added regularly.
I'm sure this must be a common problem so wondered if someone could direct me to the best solution for this?
Thanks
I think you need a bridge table here which records relationships between employee and hand-out records. Something like this:
Employee_Handout (ID, EmployeeID, HandoutID)
Every time a new handout comes out, you would insert a record into the handout table. Then, when a given employee reads that handout, you would insert a new record into the Employee_Handout table. You probably don't need to persist non-reads, since they could easily be detected as being absent from the bridge table.
The primary key for this column would probably be (EmployeeID, HandoutID), assuming you would only want a single record relating an employee reading a given handout. This would also mean that a given employee/handout relationship could only be persisted once.

how to implement one to one relationship from one table to two tables?

I have Mysql database with two tables:
One is table of payments_by_check and the other is payments_by_credit_card.
There is an option to cancel everyone of them, so I created a new table for cancellations.
every check payment or credit card payment may have record in cancellation table, and may not have it.
I don't know what is the correct way to build it, the options are:
Adding column of cancellation id in everyone of the payments table.
Adding in cancellation table one column for payment by check id, and
another one for payment by credit card id, and every record will
have one of them empty.
The payments tables are very large, so I'd rather avoid adding column to those table.
My question is:
Is it correct to take the second option?
Does it make any effect on performances?
Payments by check and payments by credit card is a classic case of what is called "generalization/specialization". This is the equivalent, roughly, of classes and subclasses in object modeling. You can find some good articles on how to include gen-spec in an ER model by searching the web.
Things get interesting when you go to implement this design with relational tables. There are two widely used approaches: Single-table-inheritance and Class-table-inheritance. There are two tabs with these names in StackOverflow. If you check the info under these tabs you'll get an overview. You can also look these up on the web. I particularly like Martin Fowler's treatment. Each alternative has its benefits and drawbacks.
In your case, I would use a Single-Table-Inheritance approach, with just one Payments table for both kinds. You'll have to have a column to say what kind each payment is, plus a few columns that only pertain to Credit card payments, and a few that only pertain to check payments.
But it's your call. If you decide to go with Class-table-inheritance instead, and you use Shared-Primary-Key to Share Ids across all three tables, you'll find that wotks pretty well,too.
As #Walter Mitty is mentioned, a normal solution to the problem will be something like:
In the case that such a restructure is not available, then:
Is it correct to take the second option?
Both 1 & 2 will have some problems, yet both can be applied based on your needs.
Solution No.1: Adding column of cancellation id to payments tables
Design problem:
A cancellation record can exist, without no related record in
payment tables.
A cancellation record can exist, with more than one related
record in payment tables.
Performance problem:
Creation of a cancellation record will need one insert inside cancellation and one update in related payment record.
Solution No.2: Having payments FK inside Cancellation table.
Haveing two null-able foreign key columns, yet one must be filled, just a check constraint is needed to achieve this.
Design problem:
Performance problem:
Detecting if a payment record is cancelled will need a query form payment joined to cancellation table.
In case of read performance No.1 is preferred.
In case of data consistency + write performance No.2 is preferred.
Another mixed solution that I will prefer is using No.2 solution plus having a column called is-cancelled in payment tables(to overcome read performance)
Payments table is large. Cancellations table is much smaller, correct? That is, Cancellations would have rows only for cancellations, not non-cancelled payments.
Cancellations has a column for JOINing to Payments, correct? So, it does not really need to include the payment_type or amount. Just cancellation_date and some admin stuff.
With two tables, LEFT JOIN or UNION can put them together when you need to see both bits of info. So, that is not a 'real' problem, just a coding nuisance.

MySQL on duplicate key delete

I am looking for a (not too convoluted) solution for a MySQL problem. Say I have the following table (with a joint index on group and item):
Group item
nogroup item_a
group_a item_a
Then, eventually, item_a no longer belongs to group_a. So I want to do something like:
update table set group = "nogroup" where item = "item_a" on duplicate key delete.
(obviously this is not a valid symtax but I am looking for a way around this)
I still want to keep a copy of the record with nogroup because, if later on, item_a comes back, i can change its group back to group_a or any other group depending on the case. Whenever item_a is added, there is an insert and it copies all the data from the nogroup record and sets a proper group label. At that point there are two records for item_a: one with group_a and one with no group. The reason it is done this way is to reuse previous data as much as possible as a new entry(with no previous record) is much more involved and take significantly more time and processing.
Say an item belongs to group_a and group_b but suddenly it does not belong to any group: the first update to set group to "nogroup" will work but the second update will create a duplicate key entry error.
The option of "not updating the group column at all" and using "insert on duplicate key update" does not work because there won't be duplicates when the groups are different and this will lead to cases where an item does not belong to a group anymore and yet the record will still be present in the database. The option of verifying if "nogroup" exists first and then updating it to a specific group does not work either because if item_a belongs to more than one group this would update all other records to the same group.
Basically, an item can belong to 1) any number of groups including "nogroup" or 2) solely belonging to "nogroup" and there should always be a copy of at least nogroup somewhere in the database.
It looks like I won't be able to do this in just one query but if someone has a clean way of dealing with this, that would be much appreciated. Maybe some of my assumptions above are wrong and there is an easy way to do it.
Your whole process of maintaining this items-to-groups mapping sounds too complicated. Why not just have a table that has a mapping? Then, when an item is removed from a group, delete it from the table. When it is added, add it to the table. Don't bother with "nogroup".
If you want an archive table, then create one. Have an insert/update/delete trigger (whichever is or are appropriate) that will populate an archive with information that you want to keep over time.
I do not understand why re-using an existing row would be beneficial in terms of performance. There is no obvious database reason why this would be the case.
I am also confused as to why you need a "nogroup" tag at all. If you need a list of items, maintain that list in its own table. And call the table Items -- a much clearer name than "nogroup".
I agree with Gordan's approach. However if you have to do it with a single table it cannot be done in 1 SQL query. You will have to use 2 queries 1 for update and 1 for delete.

The best/correct way in MySql to group records (not "Group By")

I have a table that is going to have a bunch of stock numbers and some other information. I need to be able to create groups of stock numbers e.g. products 123A,456B, and 789C all are in the same group. Each stock number has its own record in the table. I don't really care how it is accomplished so long as I can make sure that the items are grouped and that group numbers are unique. What is the best way to accomplish this?
There are a few ways I can think of to accomplish this but I'm not really sure what the best way is or if there are hidden drawbacks:
1) I could have a separate table with one auto increment column that is responsible for generating group id's and then add the Id to the stock number table once it's generated but that semes wasteful.
2) Would selecting the max groupId from the stock number table and adding one to it be good to get new group id's?
3) I could not have groupId in the stock number table and do it using a separate table with a GroupID column and a StockNumberId column. Still how would I get the groupId doing it this way?
You don't have to respond to all three. In indication of which would be the most appropriate way and any notes on implementation or pitfalls would be helpful. Also if there is a better way I did not innumerate please enlighten me. Thank you.
If you only need to set a groupId for each stock, and if each stock belongs to one and only one group then solution (2) would work just fine.
I would also create an index on the groupId column for easy retrieval of stocks that belong to the same group, as well as for getting the MAX(groupId) more efficiently.
I would use solution (1) if I had to also store group information, e.g., groupName, etc.
Solution (3) would be more towards a Many-To-Many relation between Stocks and Groups where the table you are describing sits in between the two. But in your case this would not be a good solution.

How to set up relational database tables for this many-to-many relationship?

I have a type of data called a chain. Each chain is made up of a specific sequence of another type of data called a step. So a chain is ultimately made up of multiple steps in a specific order. I'm trying to figure out the best way to set this up in MySQL that will allow me to do the following:
Look up all steps in a chain, and get them in the right order
Look up all chains that contain a step
I'm currently considering the following table set up as the appropriate solution:
TABLE chains
id date_created
TABLE steps
id description
TABLE chains_steps (this would be used for joins)
chain_id step_id step_position
In the table chains_steps, the step_position column would be used to order the steps in a chain correctly. It seems unusual for a JOIN table to contain its own distinct piece of data, such as step_position in this case. But maybe it's not unusual at all and I'm just inexperienced/paranoid.
I don't have much experience in all this so I wanted to get some feedback. Are the three tables I suggested the correct way to do this? Are there any viable alternatives and if so, what are the advantages/drawback?
You're doing it right.
Consider a database containing the Employees and Projects tables, and how you'd want to link them in a many-to-many fashion. You'd probably come up with an Assignments table (or Project_Employees in some naming conventions).
At some point you'd decide you want not only to store each project assignment, but you'd also want to store when the assignment started, and when it finished. The natural place to put that is in the assignment itself; it doesn't make sense to store it either with the project or with the employee.
In further designs you might even find it necessary to store further information about the assignment, for example in an employee review process you may wish to store feedback related to their performance in that project, so you'd make the assignment the "one" end of a relationship with a Review table, which would relate back to Assignments with a FK on assignment_id.
So in short, it's perfectly normal to have a junction table that has its own data.
That looks fine, and it's not unusual for the join table to contain a position/rank field.
Look up all steps in a chain, and get them in the right order
SELECT * FROM chains_steps
LEFT JOIN steps ON steps.id = chains_steps.step_id
WHERE chains_steps.chain_id = ?
ORDER BY chains_steps.step_position ASC
Look up all chains that contain a step
SELECT DISTINCT chain_id FROM chains_steps
LEFT JOIN chains ON chains.id = chains_steps.chain_id
I think that the plan you've outlined is the correct approach. Don't worry too much about the presence of step_position on your mapping table. After all the step_position is a bit of data that is directly related to a step in the context of a chain. So the chains_steps table is the right place for it IMHO.
Some things to think about:
Foreign keys - use 'em!
Unique key on the chains_steps table - can a step be present in more than one position in a single chain? What about in different chains?
Good luck!