I have a lists table that has an order field.
When I insert a new record, is it possible to find the order of the previous row and increment the new row?
Or should I go about it myself in PHP by doing an OrderBy('order') query and getting the max() value of that?
When you declare a table with MySQL you can use an auto-increment id so you won't have to deal about its incrementation:
CREATE TABLE people (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
);
As explained in the documentation,
An integer or floating-point column can have the additional attribute
AUTO_INCREMENT. When you insert a value of NULL (recommended) or 0
into an indexed AUTO_INCREMENT column, the column is set to the next
sequence value. Typically this is value+1, where value is the largest
value for the column currently in the table. AUTO_INCREMENT sequences
begin with 1.
I suggest you to ommit the field completly when inserting new records.
You can then retrieve the last id inserted with LAST_INSERT_ID() SQL function (or the mysqli_insert_id function of PHP languagefor example).
But since it's not what you wanted, probably because of one of the reasons quoted from MarioZ's comment:
If you are already using auto-increment for the ID you can use it for
the order (that can be one reason). For auto-increment the column
must be set as primary and unique, can't be repeated values. The auto-increment is from the number in the record, if you inserted 10
rows and you delete 2, the next insert with auto-increment will be
11(if the last now is 8 you'd want it to be 9). Those are posible
reasons not to use it for what #Notflip wants :P
... You'll have to use PHP, with LOCK TABLE and UNLOCK TABLE SQL instructions before and after the retrieving of the last order then the updating of the new order, to avoid having simultaneous records with the same "order".
Related
I was developing a database in SQL Server where I was using an identity column as a seed for a primary key field. The intention was to reset the identity to 1 at the beginning of every year. This would allow us to create a PK of the Year - Identity Column.
Create Table Issues (
IssueID AS RIGHT(CONVERT(VARCHAR, Year(getdate()), 4),2) + '-' + RIGHT(REPLICATE('0', 2) +
CONVERT(VARCHAR, RecordID),3) NOT NULL PRIMARY KEY,
RecordID int Identity (1,1),.........)
The result would be
IssueID RecordID
20-001 1
20-002 2
20-003 3
21-001 1
etc....
Now I've been told we are going to use a MySQL database instead.
Can an Auto-Increment field in MySQL contain duplicate values like it can in SQL Server?
If Not, how can I do what I need to do in MySQL?
In MySQL, you can't use the default auto-increment feature for what you describe, a incrementing value that starts over per year.
This was a feature of the MyISAM storage engine years ago. An auto-increment that was the second column of a multi-column primary key would start counting from one for each distinct value in the first column of the PK. See the example under "MyISAM Notes" on this page: https://dev.mysql.com/doc/refman/8.0/en/example-auto-increment.html
But it's considered not a good idea to use MyISAM because it does not support ACID. In general, I would find another way of solving this task. I would not use MyISAM.
In InnoDB, there's no way the table will generate a value that is a duplicate of a value currently in the table, or even a value less than the maximum value previously generated for that table. In other words, there's no way to "fill in the gaps" using auto-increment.
You can use ALTER TABLE mytable AUTO_INCREMENT=1 to reset the counter, but the value you set it will automatically advance to the max value currently in the table + 1.
So you'll have to generate it using either another table, or else something other than the MySQL database. For example, I've seen some people use memcached, which supports an atomic "increment and return counter" operation.
Another thing to consider: If you need a row counter per year, this is actually different from using MySQL's auto-increment feature. It's not easy to use the latter as a row counter. Besides, what happens if you roll back a transaction or delete a row? You'd end up with non-consecutive RecordId values, with unexplained "gaps." It's also a fact about the auto-increment feature that it guarantees that subsequent id's will be greater, but it does not guarantee to generate all values consecutively. So you'll get gaps eventually anyway.
In MySQL a table can have only one auto_increment column and this column must be a part of the primary key. See details here.
Technical workaround for your task would be creating of a table with a single auto_increment column, and you can obtain auto_increment value by inserting a record into this table and immediately calling standard MySQL function last_inser_id(). When time comes you should truncate the table - in this case the auto_increment count will be reset.
I have a table that has a primary key and for some reason i was advised not to use the AUTO_INCREMENT flag.
So every time i have to insert a new entry i search for the last value inserted (highest value) of the primary key.
Then i increment it by 1 to get a new ID.
Now the problem i face is, when inserting the first entry, there is no data in the table.
Can anyone suggest the optimal way to check
if data exists in table,
if not set id as 1 and insert new row,
else get the last id from table, increment it by 1 and then insert new row.
PS: New to mysql so having difficulty with its syntax.
Based on your statement 'i search for the last value inserted (highest value) of the primary key', I presume that you're currently doing something like this to get the maximum existing ID:
SELECT MAX(id_column) + 1 FROM my_table
If you have an empty table, this will of course return NULL. In that case, just handle the NULL using IFNULL to return 0 if there is no maximum value:
SELECT IFNULL(MAX(id_column), 0) + 1 FROM my_table
This will output 1 as the next identifier if the table has no rows.
I've given this more thought, and it turns out there is a way to generate a unique primary key without using Auto Increment or worrying about race conditions, so long as you are willing and able to use a 36 byte primary key (or, alternatively, a 128 bit binary).
The solution (at least as of MySQL 5.5) is the UUID, which stands for Universal Unique Identifier.
You would use it thus:
CREATE TABLE uu_table (
id VARCHAR(36) PRIMARY KEY,
name VARCHAR(50),
{other interesting columns}
)
Then insert new rows thus:
INSERT INTO uu_table VALUES (UUID(), 'Name of this Row', {other interesting values});
The UUID() function is guaranteed to generate a unique key 99.99{bunch more 9's}% of the time, even if generated on independent systems. That's its whole purpose, to be as unique as snowflake patterns, no matter where it is created.
There are pros and cons to this method. Best to read up on it here: https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_uuid
It is basically a 128 bit number, which you can save as a binary value after conversion from the 36 characters. I believe some versions of MySQL come with functions for that purpose. That would use less space in your database than 36 bytes, but I'll leave that as an exercise for the reader.
There is a necessity when inserting into a table of values to change the auto-increment field to another that no two of the same id in these tables. It is necessary for the data output from the third table based on the recording and going to not add to the table a column indicating. Here's my trigger, but it does not work
CREATE TRIGGER `update_id` AFTER INSERT ON `table1`
FOR EACH ROW BEGIN
ALTER TABLE `table2` AUTO_INCREMENT = NEW.id;
END;
It's not entirely clear what problem you are trying to solve.
But it sounds as if you have two tables with an id column, and you want to ensure that the same value of id is not used in both tables. That is, if id value 42 exists in table1, you want to ensure that 42 is not used as an id value in table2.
Unforunately, MySQL does not provide any declarative constraint for this.
It sounds as if you want an Oracle-style SEQUENCE object. And unfortunately, MySQL doesn't provide an equivalent.
But what we can do is emulate that. Create an extra "sequence" table that contains an AUTO_INCREMENT column. The purpose of this table is to be used to generate id values, and to keep track of the highest generated id value:
CREATE TABLE mysequence (id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY);
Then, we'd remove the AUTO_INCREMENT attribute from the id columns of the two tables we want to generate distinct id values for.
For those tables, we'd create BEFORE INSERT triggers that will obtain distinct id values and assign it to the id column. To generate a unique value, we can insert a row to the new mysequence table, and then retrieve the auto_increment value using the LAST_INSERT_ID function.
Something like this:
CREATE TRIGGER table1_bi
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
DECLARE generated_id INT UNSIGNED;
-- do we need to generate a value for id column?
IF NEW.id IS NULL THEN
-- generate unique id value with insert into sequence table
INSERT INTO mysequence (id) VALUES (NULL);
-- retrieve inserted id value
SELECT LAST_INSERT_ID() INTO generated_id;
-- assign the retrieved value to the id columns of the row being inserted
SET NEW.id = generated_id;
END IF
END$$
(That's just a rough outline, likely there's at least one syntax error in there somewhere.)
You'd need to create a BEFORE INSERT trigger for each of the tables.
This is one approach to generating distinct values for the id columns.
Note that it wouldn't be necessary to keep ALL of the rows in the mysequence table, it's only necessary to keep the row with the largest id value.
Also note that this doesn't enforce any constraint on either tables; some session could supply a value for id that is already in the other table. To prevent that, the trigger could raise an error if a non-NULL id value is supplied. It might also be possible to allow non-NULL values, and to perform a query to check if the supplied id value already exists in the other table, and raise an error if it does. But that query would be subject to a race condition... two concurrent sessions doing inserts to the tables, and you'd need to implement some concurrency killing locking mechanisms to prevent concurrent inserts.
I am using mysql, and am looking at a strange behavior.
Scenario :
I have a table having table_id as primary key, which is set to auto-increment.
table_id more_columns
1 some value
2 others
Now if i delete row 2, and insert one more row, the table_id becomes 3 (Expected is 2)
table_id more_columns
1 some value
3 recent
Why is it so? Here I am loosing some ids (I know they are not important). Please put some lights on this behavior
In auto-increment field If a row is deleted, the auto_increment column of that row will not be re-assigned.
Please see here for more information.
For reasons why auto-increment doesn't use deleted values you can refer here(mentioned in comments by #AaronBlenkush).
The auto_increment value is a counter stored internally for each table. The counter is only increased, never decreased.
Every syntactically correct INSERT statement fired against the table increments this counter, even when it is rolled back and also when you define an insert value for the primary key.
A MySQL auto_increment column maintains a number internally, and will always increment it, even after deletions. If you need to fill in an empty space, you have to handle it yourself in PHP, rather than use the auto_increment keyword in the table definition.
Rolling back to fill in empty row ids can cause all sorts of difficulty if you have foreign key relationships to maintain, and it really isn't advised.
The auto_increment can be reset using a SQL statement, but this is not advised because it will cause duplicate key errors.
-- Doing this will cause problems!
ALTER table AUTO_INCREMENT=12345;
EDIT
To enforce your foreign key relationships as described in the comments, you should add to your table definition:
FOREIGN KEY (friendid) REFERENCES registration_table (id) ON DELETE SET NULL;
Fill in the correct table and column names. Now, when a user is deleted from the registration, their friend association is nulled. If you need to reassociate with a different user, that has to be handled with PHP. mysql_insert_id() is no longer helpful.
If you need to find the highest numbered id still in the database after deletion to associate with friends, use the following.
SELECT MAX(id) FROM registration_table;
After delete write this query
ALTER TABLE tablename AUTO_INCREMENT = 1
I have a 2 columns in my table: a varchar(8) and an int.
I want to auto-increment the int column and when I do, I want to copy the value into the varchar(8) column, but pad it with 0's until it is 8 characters long, so for example, if the int column was incremented to 3, the varchar(8) column would contain '00000003'.
My two questions are, what happens when the varchar(8) column gets to '99999999' because I don't want to have duplicates?
How would I do this in MySQL?
If my values can be between 00000000 to 99999999, how many values can i have before I run out?
This is my alternative approach to just creating a random 8 character string and checking MySQL for duplicates. I thought this was a better approach and would allow for a greater number of values.
Because your formatted column depends upon, and is derivable from, the id column, your table design violates 3NF.
Either create a view that has your derived column in it (see this in sqlfiddle):
CREATE VIEW myview AS
SELECT *, substring(cast(100000000 + id AS CHAR(9)), 2) AS formatted_id
FROM mytable
or just start your auto-increment at 10000000, then it will always be 8 digits long:
ALTER TABLE mytable AUTO_INCREMENT = 10000000;
Simple, if the column is unique, it will throw an exception telling that the value already do exists. But if not unique, after 99999999 you'll get error message that the value is truncated.
Alternatives, why not use INT AUTO_INCREMENT? or a custom ID with a combination of date/time, eg
YYMMDD-00000
This will have a maximum record of 99999 records per day. It will reset on the next day.