MySQL two insert statements in one transaction with a dependency - mysql

Say there are three tables for your bakery business:
online_order
invoice
customer_id
Your boss calls you and says "I need to order a chocolate cake for all of our clients, invoice them all separately as well". So now you have to do two things:
Create an online_order entry for each client
Then invoice them for that order.
However, each client needs to have their OWN online_order number that they can reference on their invoice.
How can you do this in one transaction? Below is what I have (psuedocode) but I'll show you my output and expected output.
BEGIN;
-- First lets create the order
INSERT INTO online_order (online_order_id, date, cake_type, cake_flavor)
VALUES
(AUTO_INCREMENT, NOW(), round, chocolate);
-- Then take that online_order_id and create an invoice out of it for one of our customers
INSERT INTO invoice (date, online_order_id, invoice_amt, customer_id)
SELECT
NOW(),
LAST_INSERT_ID(),
10.00,
CUSTOMER_ID
FROM CUSTOMER;
ROLLBACK;
END;
My output
-- Let's say the next auto-incremented online_order_id is 10011:
INSERT INTO invoice VALUES ("2016-01-01", 10011, 10.00, "1");
INSERT INTO invoice VALUES ("2016-01-01", 10011, 10.00, "2");
INSERT INTO invoice VALUES ("2016-01-01", 10011, 10.00, "3");
INSERT INTO invoice VALUES ("2016-01-01", 10011, 10.00, "4");
INSERT INTO invoice VALUES ("2016-01-01", 10011, 10.00, "5");
Desired output (different online_order_id for each invoice)
INSERT INTO invoice VALUES ("2016-01-01", 10011, 10.00, "1");
INSERT INTO invoice VALUES ("2016-01-01", 10012, 10.00, "2");
INSERT INTO invoice VALUES ("2016-01-01", 10013, 10.00, "3");
INSERT INTO invoice VALUES ("2016-01-01", 10014, 10.00, "4");
INSERT INTO invoice VALUES ("2016-01-01", 10015, 10.00, "5");
I need to insert an order, then insert an invoice, then insert an order, then insert an invoice.... for each client. How can I write this in MySQL? Thank you!

You can create a table to maintain the IDs for different entity and use SELECT.. FOR UPDATE to get the atomic values. Table would look like this:
Entity | Value
Order | 1
Blah | 2
You can then use the following block to get the new id:
set #id := select value from Ids where Entity = 'Order' FOR UPDATE;
update Ids set value = value + 1 Enity = 'Order';
commit;
This would give you the id and increment it by 1. This id then can be used in invoide table.
As you will be using select.. for update, corresponding row will be locked and no other transaction will be able to read/modify the value, resulting in atomicity.
This approach might prove to be a bottleneck if you have too many insertions executing in parallel.

Your pseudocode doesn't make much sense - you should only rollback the transaction if it fails. But leaving that aside for now, inserting one row at a time is very innefficient. I would go with something like....
INSERT INTO invoice
(date, online_order_id, invoice_amt, customer_id, reference)
SELECT
NOW(), LAST_INSERT_ID(), 10.00, CUSTOMER_ID, CONNECTION_ID()
FROM CUSTOMER;
INSERT INTO online_order
(date, cake_type, cake_flavor)
SELECT NOW(), 'round', 'chocolate'
FROM invoice
WHERE reference=CONNECTION_ID();
UPDATE invoice SET reference=NULL
WHERE reference=CONNECTION_ID();
The above ensures most of the consistency without the use of a transaction at all, although with a procedure you could create a variable and assign its value from uuid() those obviating the need for the final update and keeping an index on reference well balanced.

Related

MySQL DB Trigger Insert after cost linked to id

first Trigger I created.
I have two tables, student and cost.
If I insert a new student I want to automatically insert a cost row for this student in the table cost and insert the corresponding student id to the cost.
I donĀ“t know how I can link the student id to the cost...
CREATE TRIGGER `add_cost` AFTER INSERT ON `student` FOR EACH ROW INSERT INTO cost (amount) VALUES (2000)
Thanks everybody!!
In your insert statement please use just:
NEW.{primary_key}
from related table(in your case student). If your primary is id this should looks like:
NEW.id
NEW is created after row is inserted and contain all inserted values + PK after insert.
Finally your query should looks like below:
INSERT INTO cost (student_id, amount) VALUES (NEW.id, 2000)
You can find more info on http://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html

Can an auto-incremented value in MySQL be preserved?

How do I preserve the last_insert_id() into a specific record?
Here are my queries:
INSERT INTO student(student_name) VALUES ('ramgopal')`;
SELECT `last_insert_id()`;
INSERT INTO grades (student_id,grade) VALUES (last_insert_id(),'A');
INSERT INTO class (semester,day,time) VALUES ('spring 2015','tuesday','12');
SELECT last_insert_id();
insert INTO grades(class_id) VALUES (last_insert_id());
In grade table I got grade_id, class_id, student_id and grade records.
When I use the last_insert_id() again it is just inserting the latest id and the previous id is not inserting. So I would like to preserve the initial last_insert_id for further use.

2 Dates from the same column in one SQL query

I'm trying to work out an average between of time between two dates in the same field.
Basically i've got a transaction date and an id for each transaction and a customer id for each transaction.
I need to get the time between the first transaction and the second transaction. I dont mind working out the average between the two in excel but I dont know how to pull two dates from the same field.
transaction.created_at of the first transaction minus transaction.created_at of the second transaction for each and every customer in the database. I can pull the date of a transaction like
select
customer.id,
transaction.created_at
count(transaction.id)
from transaction
having count(transaction.id) = 2
Thanks
Not sure if this will always be Having count(*) = 2? If so, I think you could just use min and max, no?
/*
create table dbo.Example (tran_id int,created_at datetime,cust_id int)
insert dbo.example values (1,'10/1/2012',900)
insert dbo.example values (2,'10/2/2012',901)
insert dbo.example values (3,'10/18/2012',590)
insert dbo.example values (4,'10/10/2012',676)
insert dbo.example values (5,'10/11/2012',123)
insert dbo.example values (6,'10/17/2012',456)
insert dbo.example values (7,'10/9/2012',901)
insert dbo.example values (8,'10/30/2012',900)
insert dbo.example values (9,'10/4/2012',456)
insert dbo.example values (10,'10/17/2012',676)
*/
select
cust_id,
max([created_at]) as [Last Date],
min([created_at]) as [First Date],
datediff(hh,min([created_at]) ,max([created_at])) as [Hours diff]
from example
group by cust_id
having count(*) = 2
order by cust_id
Try the following to retrieve particular customer transactions count:
Select
Customer.id,
count(transaction.id)
from transaction
where Customer.id = '10'
Here 10 specifies the customer id that has been searched by you. You have to pass this customer id as parameter in your created SP.

SQL `update` if combination of keys exists in row - else create new row

First of, I've searched this topic here and elsewhere online, and found numorous articles and answers, but none of which did this...
I have a table with ratings, and you should be able to update your rating, but not create a new row.
My table contains: productId, rating, userId
If a row with productId and userId exists, then update rating. Else create new row.
How do I do this?
First add a UNIQUE constraint:
ALTER TABLE tableX
ADD CONSTRAINT productId_userId_UQ
UNIQUE (productId, userId) ;
Then you can use the INSERT ... ON DUPLICATE KEY UPDATE construction:
INSERT INTO tableX
(productId, userId, rating)
VALUES
(101, 42, 5),
(102, 42, 6),
(103, 42, 0)
ON DUPLICATE KEY UPDATE
rating = VALUES(rating) ;
See the SQL-Fiddle
You are missing something or need to provide more information. Your program has to perform a SQL query (a SELECT statement) to find out if the table contains a row with a given productId and userId, then perform a UPDATE statement to update the rating, otherwise perform a INSERT to insert the new row. These are separate steps unless you group them into a stored procedure.
Use a REPLACE INTO query.
REPLACE INTO table (productId, userId, rating) VALUES ('product id', 'user id', 'rating');
REPLACE INTO is like a normal insert, except if it finds a row already in the table with the same unique key it will delete that row before inserting.

Identify transaction with default value table SQLServer

I have a table where I do only INSERT operations.
I'd like to uniquely identify these transactio operations through a unique default value (I could do some sequentially INSERT in a transaction that i want to identify with a same id).
Example:
BEGIN
INSERT INTO Table_Example (Id, Value)
VALUES (1,'TEST'), (2,'TEST 2'), (3,'TEST 3)
INSERT INTO Table_Example (Id, Value)
VALUES (4,'TEST 4'), (5,'TEST 5'), (6,'TEST 6)
END
Result in Table_Example (Session_Id has been set as i'd like to do)
Id | Value | Session_Id
1 TEST sakmfnsakjnfms-jkfhsajfs-1
...
6 TEST 6 sakmfnsakjnfms-jkfhsajfs-1
this can be accomplished through such a function or is there any best practices?