Advanced use of auto-increment? - php

I'll try and make this question as brief as possible. I was wondering if there is a way to do the following via PHP & MySQL coding:
Delete an auto-incremented id entry (say entry 12 of 20), and at the same time lower the rest of the entries by 1. (Example: Delete entry 12 == Entry 13 becomes 12, 14 becomes 13, etc. etc.)
Manually (i.e via form) insert an entry with an ID so that the rest are pushed up if specified by the user (and defaults to normal behaviour if nothing is specified. (Example: User specifies that he wants his entry to be ID 14, but there is already an ID 14 in the database. What should happen would be that the user input pushes the already existing entry up to 15, & the rest of the existing entries follow a similar fashion. If the user does not specify an ID, default to normal behaviour.)
I'm still learning MySQL & PHP and the code I need is for an image gallery system that I've been building for a while now.
Is it at all possible?

That is really not an efficient way to use auto-increment columns (databases are not designed to do that). You'd wind up doing effectively a table scan any time you inserted or removed an item.
If you want to have an ordering that you can insert at will, it'd be better to maintain a separate mapping (e.g. have a separate entry that stores a list of IDs that you can manipulate at will).

For task 1, you would need to manually set the AUTO_INCREMENT value for that table after modifying all of the IDs so there's no gap when the next insert happens. A far better way is to not use an auto-increment ID at all - have an ID column, and use MAX(id)+1 to find the required ID at the time of record insertion.
here's my psuedocode:
for a deletion:
$n = 12;
DELETE from items where id='$n';
UPDATE items SET id=id-1 WHERE id > '$n'
for a specified insertion:
$n = 15;
(if there is a record with id = 15)
UPDATE items SET id=id+1 WHERE id >= '$n'
INSERT into items (id,..) values ('$n',...)
for an unspecified insertion
$n = SELECT MAX(id)+1 FROM items
INSERT into items (id,..) values ('$n',...)

For #1 try this SQL commands:
DELETE FROM table WHERE id=12;
UPDATE table set id=id-1 WHERE id > 12;
ALTER TABLE table AUTO_INCREMENT=AUTO_INCREMENT-1;
But it is a bad idea to use ID field like this. You shouldn't modify it's value after row was inserted into table.

You can use this for your 1) question. Run the whole block together. #id should be set to the row you want to delete.
SET #id = 12;
DELETE FROM tbl WHERE id = #id;
UPDATE tbl set id = id-1 WHERE id > #id;
ALTER TABLE tbl AUTO_INCREMENT = 0;
MySQL disclaimer: You cannot reset the counter to a value less than or equal to any that have already been used. For MyISAM, if the value is less than or equal to the maximum value currently in the AUTO_INCREMENT column, the value is reset to the current maximum plus one. For InnoDB, you can use ALTER TABLE ... AUTO_INCREMENT = value as of MySQL 5.0.3, but if the value is less than the current maximum value in the column, no error occurs and the current sequence value is not changed.

Related

Laravel MYSQL if DB::commit not added skips id and when added takes subsequent one [duplicate]

I'm using MySQL's AUTO_INCREMENT field and InnoDB to support transactions. I noticed when I rollback the transaction, the AUTO_INCREMENT field is not rollbacked? I found out that it was designed this way but are there any workarounds to this?
It can't work that way. Consider:
program one, you open a transaction and insert into a table FOO which has an autoinc primary key (arbitrarily, we say it gets 557 for its key value).
Program two starts, it opens a transaction and inserts into table FOO getting 558.
Program two inserts into table BAR which has a column which is a foreign key to FOO. So now the 558 is located in both FOO and BAR.
Program two now commits.
Program three starts and generates a report from table FOO. The 558 record is printed.
After that, program one rolls back.
How does the database reclaim the 557 value? Does it go into FOO and decrement all the other primary keys greater than 557? How does it fix BAR? How does it erase the 558 printed on the report program three output?
Oracle's sequence numbers are also independent of transactions for the same reason.
If you can solve this problem in constant time, I'm sure you can make a lot of money in the database field.
Now, if you have a requirement that your auto increment field never have gaps (for auditing purposes, say). Then you cannot rollback your transactions. Instead you need to have a status flag on your records. On first insert, the record's status is "Incomplete" then you start the transaction, do your work and update the status to "compete" (or whatever you need). Then when you commit, the record is live. If the transaction rollsback, the incomplete record is still there for auditing. This will cause you many other headaches but is one way to deal with audit trails.
Let me point out something very important:
You should never depend on the numeric features of autogenerated keys.
That is, other than comparing them for equality (=) or unequality (<>), you should not do anything else. No relational operators (<, >), no sorting by indexes, etc. If you need to sort by "date added", have a "date added" column.
Treat them as apples and oranges: Does it make sense to ask if an apple is the same as an orange? Yes. Does it make sense to ask if an apple is larger than an orange? No. (Actually, it does, but you get my point.)
If you stick to this rule, gaps in the continuity of autogenerated indexes will not cause problems.
I had a client needed the ID to rollback on a table of invoices, where the order must be consecutive
My solution in MySQL was to remove the AUTO-INCREMENT and pull the latest Id from the table, add one (+1) and then insert it manually.
If the table is named "TableA" and the Auto-increment column is "Id"
INSERT INTO TableA (Id, Col2, Col3, Col4, ...)
VALUES (
(SELECT Id FROM TableA t ORDER BY t.Id DESC LIMIT 1)+1,
Col2_Val, Col3_Val, Col4_Val, ...)
Why do you care if it is rolled back? AUTO_INCREMENT key fields are not supposed to have any meaning so you really shouldn't care what value is used.
If you have information you're trying to preserve, perhaps another non-key column is needed.
I do not know of any way to do that. According to the MySQL Documentation, this is expected behavior and will happen with all innodb_autoinc_lock_mode lock modes. The specific text is:
In all lock modes (0, 1, and 2), if a
transaction that generated
auto-increment values rolls back,
those auto-increment values are
“lost.” Once a value is generated for
an auto-increment column, it cannot be
rolled back, whether or not the
“INSERT-like” statement is completed,
and whether or not the containing
transaction is rolled back. Such lost
values are not reused. Thus, there may
be gaps in the values stored in an
AUTO_INCREMENT column of a table.
If you set auto_increment to 1 after a rollback or deletion, on the next insert, MySQL will see that 1 is already used and will instead get the MAX() value and add 1 to it.
This will ensure that if the row with the last value is deleted (or the insert is rolled back), it will be reused.
To set the auto_increment to 1, do something like this:
ALTER TABLE tbl auto_increment = 1
This is not as efficient as simply continuing on with the next number because MAX() can be expensive, but if you delete/rollback infrequently and are obsessed with reusing the highest value, then this is a realistic approach.
Be aware that this does not prevent gaps from records deleted in the middle or if another insert should occur prior to you setting auto_increment back to 1.
INSERT INTO prueba(id)
VALUES (
(SELECT IFNULL( MAX( id ) , 0 )+1 FROM prueba target))
If the table doesn't contain values or zero rows
add target for error mysql type update FROM on SELECT
If you need to have the ids assigned in numerical order with no gaps, then you can't use an autoincrement column. You'll need to define a standard integer column and use a stored procedure that calculates the next number in the insert sequence and inserts the record within a transaction. If the insert fails, then the next time the procedure is called it will recalculate the next id.
Having said that, it is a bad idea to rely on ids being in some particular order with no gaps. If you need to preserve ordering, you should probably timestamp the row on insert (and potentially on update).
Concrete answer to this specific dilemma (which I also had) is the following:
1) Create a table that holds different counters for different documents (invoices, receipts, RMA's, etc..); Insert a record for each of your documents and add the initial counter to 0.
2) Before creating a new document, do the following (for invoices, for example):
UPDATE document_counters SET counter = LAST_INSERT_ID(counter + 1) where type = 'invoice'
3) Get the last value that you just updated to, like so:
SELECT LAST_INSERT_ID()
or just use your PHP (or whatever) mysql_insert_id() function to get the same thing
4) Insert your new record along with the primary ID that you just got back from the DB. This will override the current auto increment index, and make sure you have no ID gaps between you records.
This whole thing needs to be wrapped inside a transaction, of course. The beauty of this method is that, when you rollback a transaction, your UPDATE statement from Step 2 will be rolled back, and the counter will not change anymore. Other concurrent transactions will block until the first transaction is either committed or rolled back so they will not have access to either the old counter OR a new one, until all other transactions are finished first.
SOLUTION:
Let's use 'tbl_test' as an example table, and suppose the field 'Id' has AUTO_INCREMENT attribute
CREATE TABLE tbl_test (
Id int NOT NULL AUTO_INCREMENT ,
Name varchar(255) NULL ,
PRIMARY KEY (`Id`)
)
;
Let's suppose that table has houndred or thousand rows already inserted and you don't want to use AUTO_INCREMENT anymore; because when you rollback a transaction the field 'Id' is always adding +1 to AUTO_INCREMENT value.
So to avoid that you might make this:
Let's remove AUTO_INCREMENT value from column 'Id' (this won't delete your inserted rows):
ALTER TABLE tbl_test MODIFY COLUMN Id int(11) NOT NULL FIRST;
Finally, we create a BEFORE INSERT Trigger to generate an 'Id' value automatically. But using this way won't affect your Id value even if you rollback any transaction.
CREATE TRIGGER trg_tbl_test_1
BEFORE INSERT ON tbl_test
FOR EACH ROW
BEGIN
SET NEW.Id= COALESCE((SELECT MAX(Id) FROM tbl_test),0) + 1;
END;
That's it! You're done!
You're welcome.
$masterConn = mysql_connect("localhost", "root", '');
mysql_select_db("sample", $masterConn);
for($i=1; $i<=10; $i++) {
mysql_query("START TRANSACTION",$masterConn);
$qry_insert = "INSERT INTO `customer` (id, `a`, `b`) VALUES (NULL, '$i', 'a')";
mysql_query($qry_insert,$masterConn);
if($i%2==1) mysql_query("COMMIT",$masterConn);
else mysql_query("ROLLBACK",$masterConn);
mysql_query("ALTER TABLE customer auto_increment = 1",$masterConn);
}
echo "Done";

copy column to other column in same table [duplicate]

Given the following table:
id | value
--------------
1 6
2 70
Is there a way to add a column that is automatically calculated based on another column in the same table? Like a VIEW, but part of the same table. As an example, calculated would be half of value. Calculated should be automatically updated when value changes, just like a VIEW would be.
The result would be:
id | value | calculated
-----------------------
1 6 3
2 70 35
Generated Column is one of the good approach for MySql version which is 5.7.6 and above.
There are two kinds of Generated Columns:
Virtual (default) - column will be calculated on the fly when a
record is read from a table
Stored - column will be calculated when a
new record is written/updated in the table
Both types can have NOT NULL restrictions, but only a stored Generated Column can be a part of an index.
For current case, we are going to use stored generated column. To implement I have considered that both of the values required for calculation are present in table
CREATE TABLE order_details (price DOUBLE, quantity INT, amount DOUBLE AS (price * quantity));
INSERT INTO order_details (price, quantity) VALUES(100,1),(300,4),(60,8);
amount will automatically pop up in table and you can access it directly, also please note that whenever you will update any of the columns, amount will also get updated.
If it is a selection, you can do it as:
SELECT id, value, (value/2) AS calculated FROM mytable
Else, you can also first alter the table to add the missing column and then do an UPDATE query to compute the values for the new column as:
UPDATE mytable SET calculated = value/2;
If it must be automatic, and your MySQL version allows it, you can try with triggers
MySQL 5.7 supports computed columns. They call it "Generated Columns" and the syntax is a little weird, but it supports the same options I see in other databases.
https://dev.mysql.com/doc/refman/5.7/en/create-table.html#create-table-generated-columns
#krtek's answer is in the right direction, but has a couple of issues.
The bad news is that using UPDATE in a trigger on the same table won't work. The good news is that it's not necessary; there is a NEW object that you can operate on before the table is even touched.
The trigger becomes:
CREATE TRIGGER halfcolumn_update BEFORE UPDATE ON my_table
FOR EACH ROW BEGIN
SET NEW.calculated = NEW.value/2;
END;
Note also that the BEGIN...END; syntax has to be parsed with a different delimiter in effect. The whole shebang becomes:
DELIMITER |
CREATE TRIGGER halfcolumn_insert BEFORE INSERT ON my_table
FOR EACH ROW BEGIN
SET NEW.calculated = NEW.value/2;
END;
|
CREATE TRIGGER halfcolumn_update BEFORE UPDATE ON my_table
FOR EACH ROW BEGIN
SET NEW.calculated = NEW.value/2;
END;
|
DELIMITER ;
You can use generated columns from MYSQL 5.7.
Example Usage:
ALTER TABLE tbl_test
ADD COLUMN calc_val INT
GENERATED ALWAYS AS (((`column1` - 1) * 16) + `column2`) STORED;
VIRTUAL / STORED
Virtual: calculated on the fly when a record is read from a table (default)
Stored: calculated when a new record is inserted/updated within the
table
If you want to add a column to your table which is automatically updated to half of some other column, you can do that with a trigger.
But I think the already proposed answer are a better way to do this.
Dry coded trigger :
CREATE TRIGGER halfcolumn_insert AFTER INSERT ON table
FOR EACH ROW BEGIN
UPDATE table SET calculated = value / 2 WHERE id = NEW.id;
END;
CREATE TRIGGER halfcolumn_update AFTER UPDATE ON table
FOR EACH ROW BEGIN
UPDATE table SET calculated = value / 2 WHERE id = NEW.id;
END;
I don't think you can make only one trigger, since the event we must respond to are different.
I hope this still helps someone as many people might get to this article. If you need a computed column, why not just expose your desired columns in a view ? Don't just save data or overload the performance with triggers... simply expose the data you need already formatted/calculated in a view.
Hope this helps...

How can I restart my autoincrement in phpmyadmin?

I have some tables in my phpmyadmin with one column that is auto incremented.
The problem is that when I delete some rows from the table (For example the element Car, with index auto incremented 1) and I create another row into the table, the new row have the index 2, but in the table there is only one row.
I want that this second element created to have the index equal to the position of the row, for example, if I have 3 rows that the third element will have the index equals to 3.
I was looking for a method that let me to use my phpMyAdmin like this but I couldn't find anything.
Is it possible? If it is true, what should I have to do? Do I have to create the table again?
This is generally a bad idea. Auto increment is used for creating unique ID of the row. Imagine you have a record "1 - John". Then you delete it and add another "1 - Jack". From the point of common database logic, it will seem that John was renamed to Jack (it has the same ID = it is the same entity) rather than it is another record. You should let DB assign new ID to each new record, even with leaving gaps after deleted records.
If you really want to do so, you can modify auto increment value using this query:
ALTER TABLE users AUTO_INCREMENT=123
but it is still not the way auto increment is designed for.
It is possible, but you shouldn't do that on production.
The SQL query is:
ALTER TABLE tablename AUTO_INCREMENT = 1
The most important part of this is to prevent overriding. For example you have an user and this user has id of 81 and if you delete this user and the database doesn't remember that this id 81 has ever been taken by an user (and for example, you have some relations - like friend lists) the user that is going to have the same ID will probably have the same data.
So basically, you don't want to reset auto increment values.
Execute this SQL sentence:
ALTER TABLE tablename AUTO_INCREMENT = 1
I struggled with this a bit too, then found in the "Operations" tab, there is a setting under "Auto Increment" where you can set the number it will start from, so you can roll it back if you want to do that.
It is possible. It is not a feature of phpmyadmin, but of mysql. Execute this
ALTER TABLE tablename AUTO_INCREMENT = 1
More info on this on stackoverflow
And in the mysql reference

How do i set the increment value of autoincrement in mysql

I am trying to develop a system to assign room numbers to tenants of a hostel upon registration, using the auto increment feature of sql.
However, it automatically increases by one after every entry. Because the hostel accommodates four people in one room, I want to change this to 4, so that after every 4 entries I get only one id/room number.
How do I go about this? I am using php and sql. If the autoincrement feature is not possible can you please suggest another way to achieve this? Thanks.
You would need:
http://dev.mysql.com/doc/refman/5.1/en/replication-options-master.html#sysvar_auto_increment_increment
It works like this:
mysql> SET ##auto_increment_increment=4;
So when you insert 4 rows, the auto increment column will be:
4,8,12,16
as best of my knowledge you cannot change the steps of auto-increment field. I suggest add another field and write a trigger to update its value based on auto-increment field (auto-increment/4).
I don't think this is possible with autoincrement..
Maybe you can do something like this:
//Pseudo code
//First you get the count of the highest id, to see how many users are in the last room.
SELECT COUNT(*) FROM table WHERE id=(SELECT id FROM table ORDER BY id DESC LIMIT 1)
//If the result of the last query is >= 4 then insert the next customer with id +1
Don't use auto_increment for this - it can't handle a situation where multiple records will share the same number and although you can reset it manually (see below) it's also not designed for a situation where numbers may get reused in a random order.
You could just have a room_number field with one of the mysql integer types (e.g. tinyint, smallint, mediumint…) or you could separate your database into two tables, one for people (each of whom have an id) and a second to map those ids to rooms.
However you do it, you'd then write a select query to check which room numbers are available before you add the person's details to the database.
You may need to read up on relational databases if that doesn't sound very clear.
If you do need to reset the auto_increment (sometimes it's nice to do it if you've filled a database with test data which you're about to wipe, and you want the real "production" data to begin at 1) you can use:
ALTER TABLE [tablename] AUTO_INCREMENT = 1
https://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html

How can I get a number from 1 that is unused in database

Hi my qouestion is how to get the first number that is not used in specific database row. The number must be betwen 1 and 9999 and must be compared with all numbers in that specific database row, so if data in my database row starts with 5, i wont to be able to get the first number that is not used ...in this case the number 1. then when I create data with number 1.. the next number I need to get is 2 and...I'm using that to create profiles, and that number is the profile number, and ewery new profile must have the first unused number in data base. How to do that. I don't know where to start. So if someone can put me on the right path for solution of this problem? Thanks.
the edit
But, I dont need the auto increment i need to user to be able choosing this number on his own, first, this first number must bee suggested to the user by placeing it in the text form. And if the user select the number that is alredy in the database my program whil let the user know that he is trying to select the number that is allredy exist. So if you understand me ...I know the basics of mysql. The problem comes when the user deletes one profil then the deleted number can't be used eny more. For that i need the functio first free unused number.
New edit
I'l try to clear up some details...Frst this is the program for human resources and the user creates the dosies of workers... when user is creating the new dosie hee needs to select the dosie number for this worker, now I need to sugest to user the first unused number for the new dosie... the dosie number is not the dosie 'id'. Dosie number must be selected manualy by user or he can let the first free number to given to the new dosie... I think this whill clear some things
You are probably talking about auto-Increment primary key of table rows. Just insert the data, without specifying this "number" and the database will automatically set it to the proper (next free) value.
Do not reuse primary keys (eg you have 1,2,3,4,5 but then delete 3 - if you reuse 3 you will not know at any future point that 3 was some other record that was actually deleted).
This, btw, is very basic database knowledge. Read some introduction tutorials on MySQL or any other SQL relational database.
You are trying to use bad the database.
May be you can look this: Finding the next available id in MySQL
First create a table with values 1 to 9999. Then, run this query once:
delete from table where id IN (select id from profiles)
This way, you get IDs that are not in the profiles table. The first one can be shown to the user. On saving the record, make sure to delete that ID from this table.
If I understood you correctly, this is what you are looking for.
If you are limited to using values 1 through 9999 I would probably setup the process as follows:
Add another table with two columns (id_tracker).
Populate id_tracker with id's 1 through 9999 defaulting is_used to 0.
Update id_tracker.is_used to 1 based on the contents of your table.
Add a delete, insert triggers to your table to update the id_tracker as necesssary.
And select empty ID's as follows SELECT id FROM id_tracker WHERE is_used = 0 ORDER BY id LIMIT 1
Here's some SQL to get you started:
create table id_tracker
(id int not null, is_used tinyint default 0, primary key (id));
delimiter |
CREATE TRIGGER your_table_delete_trigger BEFORE DELETE ON your_table
FOR EACH ROW
BEGIN
UPDATE id_tracker SET is_used = 0 WHERE id = OLD.your_table_id;
END;
|
CREATE TRIGGER your_table_insert_trigger AFTER INSERT ON your_table
FOR EACH ROW
BEGIN
UPDATE id_tracker SET is_used = 1 WHERE id = NEW.your_table_id;
END;
|
delimiter ;
** NOTE: the above is for MySQL

Categories