Faking Auto Increment Increment on a Table in MySQL Using Trigger - mysql

I have a content table in my MySQL database (a Drupal content table, for what it's worth), which has an auto incremented primary key, nid. I want to be able to implement an odd and even id solution, where content created on production has even ids and content created on dev has odd ids, to avoid id conflicts when I sync. Unfortunately, MySQL doesn't support sequences, or per-table auto increment increment values (i.e. increment by 2 only for db.node, rather than 1).
The best solution I can think of, is to have a BEFORE INSERT and AFTER INSERT trigger which sets the session value of auto_increment_increment to 2 in the BEFORE INSERT trigger, and then resets it to 1 in the AFTER INSERT trigger. Since it only sets the session variable, it shouldn't have any effect on other processes, and since it's a Drupal CMS table and nothing complicated is happening, it seems safe, even though it feels wrong.
However, I'm an intermediate MySQL Admin (at best :) ) and as I said it certainly feels hackish, so I thought I'd put this out there and see if anyone has any strong negative reactions to this, perhaps some issue I'm not foreseeing. ( And I suppose if no one does then maybe someone else will find this useful).

Here's a simple example of what you want to do - assuming there is an integer column 'seq'
in the 'my_table_name' table:
DROP trigger my_trigger_name;
CREATE TRIGGER my_trigger_name
BEFORE INSERT ON my_table_name
FOR EACH ROW
SET NEW.seq = (select ifnull(max(seq)+1,1) from source_table_name);

Related

selectively UPDATING old rows when INSERTING new ones into table (trigger issue) in MySQL

I've come across a slight issue when designing a trigger statement in our MySQL DB today:
We have the following relevant tables:
quote (quote_key, date, discount_rate, discount_period, supplier_key)
part (part_key, part_name, ...)
part_quote (pq_key, part_price, fk_quote_key, fk_part_key)
part_approval (papproval_key, is_approved, fk_part_quote_key)
Now for an explanation of the logic:
Every supplier issues quotes for different inputs into a manufacturing process (e.g. parts and components). If the price for a part is about right, they will be approved for manufacturing and can thus be used by our engineers. Since we want to be able to receive quotes from different suppliers for the same parts to enable a comparison, I've tried to model this process by using the part_quote and part_approval table.
On to my problem: If I want to approve a new part_quote, I would like the BOOL flag "is_approved" in all (or the most recent) old quotes to be automatically set to FALSE.
I tried to get this done by issuing a trigger statement:
CREATE TRIGGER update_approval BEFORE INSERT ON part_approval
FOR EACH ROW --- ??
I have some problems selecting the right rows to update:
How do I select the part_key, which will ultimately identify all relevant rows that require updating?
How do I select only the old (or most recent) rows?
I would have loved to include a screener but unfortunately, I do not have 10reps yet :/
Thank you so much in advance,
All the best,
Marius
First I will answer your question as-is to the best of my ability since it is helpful to know more about how to use triggers. Then I will explain why you shouldn't actually be using triggers.
Triggers provide access to two special aliases, which are not otherwise available: OLD and NEW. NEW lets you access the new values and works in update/insert triggers. OLD lets you access the old values and works in update/delete triggers.
For your case you would probably want something like this:
CREATE TRIGGER `update_approval` BEFORE INSERT ON `part_approval`
FOR EACH ROW
UPDATE `table_x` SET `value` = y WHERE `z` = NEW.a;
For more information and some useful examples of triggers, I would suggest reading this: http://dev.mysql.com/doc/refman/5.0/en/trigger-syntax.html
HOWEVER, triggers in MySQL cannot update the same table which they were triggered on, which is what you are looking to do. There is a good question someone had relating to this here: MySQL - Trigger for updating same table after insert and a good answer that you should use a stored procedure in this case.
I'm not sure this will solve my problem entirely, but based on your suggestions I came across a couple of helpful posts, that were able to implement similar updates within a trigger with multiple statements.
CREATE TRIGGER update_approval BEFORE INSERT ON part_approval
FOR EACH ROW BEGIN
DECLARE part_id INT;
SELECT part_key INTO part_id
FROM part p, part_quote pq, part_approval a
WHERE NEW.part_quote_key=pq.part_quote_key AND pq.part_key = p.part_key;
UPDATE part p, part_quote pq, part_approval a
SET a.is_approved=DEFAULT
WHERE pq.part_key=part_id AND a.approval_date<NEW.approval_date;
END;
will only be able to try it out on monday after the DB has been populated.
thanks for the help!

How to fix a table size in MySQL?

Is there a way or an option available in MySQL to fix the size of a table? Which will work as a circular buffer and overwrites the old data whenever a new data gets inserted. This is useful when an application is collecting the stats and a month old data becomes no longer useful.
I know programmatically this is possible but I don't want to do that, hence this request.
BTW, this is similar to a feature called capped collection in MongoDb.
If you want to throw away last month's data, just use the day-of-the-month in your primary key. Last month's "26" will be overwritten by next month's "26".
I don't believe there is a specific option to achieve this, however, you could make it work using a simple trigger. (I know it's a programmatic way but I believe it is the only pure-sql option in mysql)
CREATE TRIGGER `CapTable` BEFORE INSERT ON `MyTable` FOR EACH ROW BEGIN
SET #NumRows = (SELECT COUNT(*) FROM MyTable);
IF #NumRows > 1000 THEN
DELETE FROM MyTable ORDER BY id LIMIT 1;
/* delete the oldest row, assuming id is auto_increment */
END IF;
END;

How to avoid duplicate entries on INSERT in MySQL?

My application is generating the ID numbers when registering a new customer then inserting it into the customer table.
The method for generating the ID is by reading the last ID number then incrementing it by one then inserting it into the table.
The application will be used in a network environment with more than 30 users, so there is a possibility (probability?) for at least two users to read the same last ID number at the saving stage, which means both will get the same ID number.
Also I'm using transaction. I need a logical solution that I couldn't find on other sites.
Please reply with a description so I can understand it very well.
use an autoincrement, you can get the last id issued with the mysql_insert_id property.
If for some reason that's not doable, you can craete another table to hold the last id used, then you increment that in a transaction, and then use it as the key for your insert into the table. Got to be two transctions though, otherwise you'll have the same issue you have now. That can get messy and is an extra level of maintenance though. (reset your next id table to zero when ther are still some in teh related table and things go nipples up quick.
Short of putting an exclusive lock on the table during the insert operation (not even slightly recomended), your current solution just can't work.
Okay expanded answer based on leaving schema as it is.
Option 1 in pseudo code
StartTransaction
try
NextId = GetNextId(...)
AddRecord(NextID...)
commit transaction
catch Primary Key Violation
rollback transaction
Do the entire thing again
end
Obviously you could end up in an infinite loop here, unlikely but possible, probably run out of stack space first.
You could some how queue the requests and then attempt to process them, if successful remove from queue.
BUT make customerid an auto inc the entire problem dispappears.
It will still be the primary key, you just don't have to work out what it needs to be any more, in fact you don't supply it in the insert statement, mysql will just take care of it for you.
The only thing you have to remember is if you need the id that has been automatically created is to request it in one transaction.
So your insert query needs to be in the form
Insert SomeTable(SomeColumns) Values(SomeValues)
Select mysql_insert_id
or if multiple statements gets in the way wrap two statements in a start stransaction commit transaction pair.

MySQL intelligent default value

I'm adding stuff to a MySQL table, and each item has a position that it shows up in my system like a question number.
I could find out what the largest position is before adding a new question, +1 it, and then add it - but I was wondering if there's a more intelligent way that doesn't require a second query.
Something like INSERT INTO questions (id, position) values (0, MAX(position)).
This field is not the primary key, auto_increment is of no use to this situation.
I cannot use position as the key, because the key relates to many other things, and the position can be changed at any time.
I am a pretty confident MySQL query writer, so please don't offer any suggestions other than the question asked - I know of plenty of alternatives, this is just a syntax question.
I'm sure you get my drift!
Cheers.
Use auto_increment like most other folk do?
This is the intelligent way because it concurrency safe.
Best way would be to change the field properties within MySQL, meaning let MySql do the work without providing input.
TABLE TableName CHANGE position position INT( 11 ) NOT NULL AUTO_INCREMENT
Now when inserting, do not provide any values for this field.
INSERT INTO questions (var1, var2) values (val1,val2)
MySQL will automatically increment the position value. You can set current counter as well as reset if need be. This way you are not running any quarries at all.
something like this should work
INSERT INTO questions (id, position) SELECT 0, MAX(position)+1 FROM questions;
Obviously, you will need to handle the 0 value, as in set it for something appropriate, i just copied that value off your example.
You could also create a stored procedure to use in the query, or even have a trigger which determines the MAX(position), adds one and stores it in your newly created row.
Quite alot of options there, I would go for the above solution though (with a index on position)
There's no way to set a default value to a specific calculation, it seems.

MySQL Update entire table with unknown # of rows and clear the rest

I'm pretty sure this particular quirk isn't a duplicate so here goes.
I have a table of services. In this table, I have about 40 rows of the following columns:
Services:
id_Services -- primary key
Name -- name of the service
Cost_a -- for variant a of service
Cost_b -- for variant b of service
Order -- order service is displayed in
The user can go into an admin tool and update any of this information - including deleting multiple rows, adding a row, editing info, and changing the order they are displayed in.
My question is this, since I will never know how many rows will be incoming from a submission (there could be 1 more or 100% less), I was wondering how to address this in my query.
Upon submission, every value is resubmitted. I'd hate to do it this way but the easiest way I can think of is to truncate the table and reinsert everything... but that seems a little... uhhh... bad! What is the best way to accomplish this?
RE-EDIT: For example: I start with 40 rows, update with 36. I still have to do something to the values in rows 37-40. How can I do this? Are there any mysql tricks or functions that will do this for me?
Thank you very much for your help!
You're slightly limited by the use case; you're doing insertion/update/truncation that's presented to the user as a batch operation, but in the back-end you'll have to do these in separate statements.
Watch out for concurrency: use transactions if you can.