second auto increment in mysql - mysql

I have a mySQL (InnoDB) table:
belegID int NOT_NULL PRIMARY_KEY AUTO_INCREMENT,
docNum int NOT_NULL,
docYear date NOT_NULL,
.... -- more columns
I need the following relations:
belegID is the primary key (unique ID).
docNum is also a (unique) ID but related to the year. Each year starts by 1.
How can I solve this on mySQL side and how can I get the next docNum (ID related to the year)?
Thank you in advance.

You can use triggers to resolve your issue. Here is simple example (don't forget to rename schema and table if you'll try to use that):
DELIMITER //
CREATE TRIGGER fillDocNumber BEFORE INSERT ON test.t
FOR EACH ROW
BEGIN
SET #maxNum = (SELECT MAX(docNum) FROM test.t WHERE YEAR(docYear)=YEAR(NEW.docYear));
IF #maxNum IS NULL THEN
SET NEW.docNum=1;
ELSE
SET NEW.docNum=#maxNum+1;
END IF;
END;//
DELIMITER ;
Let's assume we have table:
CREATE TABLE `t` (
`belegID` int(11) unsigned NOT NULL AUTO_INCREMENT,
`docNum` int(11) unsigned DEFAULT NULL,
`docYear` date NOT NULL,
PRIMARY KEY (`belegID`)
) ENGINE=InnoDB;
After creating our trigger we'll populate it with some data:
mysql> insert into t (docYear) values ('2013-04-06');
Query OK, 1 row affected (0.13 sec)
mysql> insert into t (docYear) values ('2012-02-18');
Query OK, 1 row affected (0.05 sec)
mysql> insert into t (docYear) values ('2013-12-11');
Query OK, 1 row affected (0.02 sec)
mysql> insert into t (docYear) values ('2014-10-30');
Query OK, 1 row affected (0.07 sec)
mysql> insert into t (docYear) values ('2014-01-03');
Query OK, 1 row affected (0.06 sec)
and now it's our result:
mysql> select * from t;
+---------+--------+------------+
| belegID | docNum | docYear |
+---------+--------+------------+
| 1 | 1 | 2013-04-06 |
| 2 | 1 | 2012-02-18 |
| 3 | 2 | 2013-12-11 |
| 4 | 1 | 2014-10-30 |
| 5 | 2 | 2014-01-03 |
+---------+--------+------------+
5 rows in set (0.00 sec)
Quite as expected.
Hint: rename field docYear, it's confusing. Correct name would be docDate (or store year in it and document's date in separate field - in fact, that will increase performance since then you'll be able to create index by it and get rid of YEAR() function in calculating MAX within trigger - and so index will be used).

Related

How to modify a column to insert consequtive numbers?

So I have a table where a column that was given an auto_increment value accidentally got started form 300 instead of 1,2,3,4......i'm a beginner and i do not know how to change it back to 1,2,3,4......screenshot of table
how to change the 307, 308 to 1,2,3,4...?
I tried to update the table but that did not work.
Step-1) First take backup of your table data.
Step-2) Truncate the table by using the below SQL query.
TRUNCATE TABLE [Your_Table_Name];
Step-3) then again insert the into your table using backup data.
Alter table to drop the auto_increment, update, alter table to add the auto_increment
drop table if exists t;
create table t
( id int auto_increment primary key, val int);
insert into t values
(307,1),(308,1),(309,1),(310,1),(311,1);
alter table t
modify column id int;
#drop primary key;
show create table t;
update t
set id = id - 306;
alter table t
modify column id int auto_increment;
show create table t;
https://dbfiddle.uk/eBQh6cj8
With MySQL 8.0 you can use a window function to calculate the row numbers and then update the table:
mysql> select * from t;
+-----+------+
| id | val |
+-----+------+
| 307 | 1 |
| 308 | 1 |
| 309 | 1 |
| 310 | 1 |
| 311 | 1 |
+-----+------+
mysql> with cte as ( select id, row_number() over () as rownum from t )
-> update t join cte using (id) set id = rownum;
Query OK, 5 rows affected (0.00 sec)
Rows matched: 5 Changed: 5 Warnings: 0
mysql> select * from t;
+----+------+
| id | val |
+----+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
+----+------+
Then make sure the next id won't be a high value:
mysql> alter table t auto_increment=1;
You can try to set the auto_increment to 1, MySQL will automatically advances that to the highest id value in the table, plus 1.
Be aware that this doesn't guarantee subsequent rows will use consecutive values. You can get non-consecutive values if:
You insert greater values explicitly, overriding the auto-increment.
You roll back transactions. Id values generated by auto-increment are not recycled if you roll back.
You delete rows.
Occasionally InnoDB will skip a number anyway. It does not guarantee consecutive values — it only guarantees unique values. You should not rely on the auto-increment to be the same as a row number.
Here is a one approach to your problem.
Please take note of the following points before proceeding:
Take backup of your table in-case things do not go as expected.
Below test case has been performed on MySQL 5.7 and MyISAM Engine.
Step1: Generating dummy test table as per your test case.
mysql> CREATE TABLE t (
-> `Id` int(11) NOT NULL AUTO_INCREMENT,
-> `product_id` int(11) DEFAULT 0,
-> PRIMARY KEY (`Id`)
-> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.03 sec)
-- Inserting dummy data
mysql> INSERT INTO t VALUES (300,1);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO t VALUES (302,1);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO t VALUES (305,1);
Query OK, 1 row affected (0.00 sec)
-- Checking auto_increment value
mysql> show create table t;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| t | CREATE TABLE `t` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) DEFAULT '0',
PRIMARY KEY (`Id`)
) ENGINE=MyISAM AUTO_INCREMENT=306 DEFAULT CHARSET=latin1 |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> INSERT INTO t (product_id) VALUES (2);
Query OK, 1 row affected (0.01 sec)
-- Below is the resultant table for which we need Id starting from 1,2,3 and so on...
mysql> SELECT * FROM t;
+-----+------------+
| Id | product_id |
+-----+------------+
| 300 | 1 |
| 302 | 1 |
| 305 | 1 |
| 306 | 2 |
+-----+------------+
4 rows in set (0.00 sec)
Step2: Remove AUTO_INCREMENT for the column and set the Ids manually.
-- Remove AUTO_INCREMENT
mysql> ALTER TABLE t MODIFY COLUMN Id int(11) NOT NULL;
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
-- Set the Id manually starting from 1
mysql> SET #i = 0;UPDATE t SET id = #i :=#i +1;
Query OK, 0 rows affected (0.00 sec)
Query OK, 5 rows affected (0.00 sec)
Rows matched: 5 Changed: 5 Warnings: 0
-- Below is the updated table with Id starting from 1,2,3 and so on...
mysql> SELECT * FROM t;
+----+------------+
| Id | product_id |
+----+------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
+----+------------+
5 rows in set (0.00 sec)
Step3: Enable AUTO_INCREMENT again for future record insertions.
-- Enable AUTO_INCREMENT again for future record insertions.
mysql> ALTER TABLE t MODIFY COLUMN Id int(11) NOT NULL AUTO_INCREMENT;
Query OK, 5 rows affected (0.01 sec)
Records: 5 Duplicates: 0 Warnings: 0
-- Set the AUTO_INCREMENT value to continue from highest value of id in the table.
mysql> SELECT MAX(id+1) FROM t;
+-----------+
| MAX(id+1) |
+-----------+
| 6 |
+-----------+
1 row in set (0.00 sec)
mysql> ALTER TABLE t AUTO_INCREMENT=6;
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
-- Table is successfully modified and will have future records inserted with no gaps in Id's
mysql> INSERT INTO t (product_id) VALUES (5);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM t;
+----+------------+
| Id | product_id |
+----+------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
| 6 | 5 |
+----+------------+
6 rows in set (0.00 sec)
The DBCC CHECKIDENT management command is used to reset identity counter
DBCC CHECKIDENT (table_name [, { NORESEED | { RESEED [, new_reseed_value]}}])
[ WITH NO_INFOMSGS ]
EXample:
DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO
many times we need to just reseed to next Id available
declare #max int
select #max=max([Id]) from [TestTable]
if #max IS NULL --check when max is returned as null
SET #max = 0
DBCC CHECKIDENT ('[TestTable]', RESEED, #max)
This will check the table and reset to the next ID.
You can get help from the link below:
Reset identity seed after deleting records in SQL Server
My mother says: the mountain that can be seen is not far away, don't stop trying

Alter the LAST_INSERT_ID() from within a TRIGGER in MySQL

I have a BEFORE INSERT TRIGGER which is used to calculate the AUTO_INCREMENT value of a column (id_2).
id_1 | id_2 | data
1 | 1 | 'a'
1 | 2 | 'b'
1 | 3 | 'c'
2 | 1 | 'a'
2 | 2 | 'b'
2 | 3 | 'c'
2 | 4 | 'a'
3 | 1 | 'b'
3 | 2 | 'c'
I have PRIMARY(id_1, id_2) and I am using InnoDB. Before, the table was using MyISAM and I've had no problems: id_2 was set to AUTO_INCREMENT, so each new entry for id_1 would generate new id_2 on its own. Now, after switching to InnoDB, I have this trigger to do the same thing:
SET #id = NULL;
SELECT COALESCE(MAX(id_2) + 1, 1) INTO #id FROM tbl WHERE id_1 = NEW.id_1;
SET NEW.id_2= #id;
It works perfectly, except now the LAST_INSERT_ID() has wrong value (it returns 0). A lot of code depends on the LAST_INSERT_ID() being correct. However since MySQL 5.0.12 any changes made to LAST_INSERT_ID within TRIGGERS are not affecting the global value. Is there any way to bypass this? I can easily set the AFTER UPDATE TRIGGER which changes the LAST_INSERT_ID by calling LAST_INSERT_ID(NEW.id_2), however any client-side would get LAST_INSERT_ID set to 0.
Is there any working work-around to force MySQL to maintain the state of LAST_INSERT_ID which was changed inside the trigger? Is there any alternative, other than switching back to MyISAM which supports this out of the box or running another SELECT max(id_2) FROM tbl WHERE id_1 = :id as part of the transaction to ensure that the row found will be the one inserted earlier?
> SHOW CREATE TABLE tbl;
CREATE TABLE `tbl` (
`id_1` int(11) NOT NULL,
`id_2` int(11) NOT NULL,
`data` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id_1`,`id_2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
Example:
INSERT INTO tbl (id_1, id_2, data) VALUES (1, NULL, 'd');
SELECT LAST_INSERT_ID();
The first statement will insert the row 1 | 4 | 'd' into the table. The second statement will return 0, but I need it to return 4.
As asked by Ravinder Reddy, adding the short explanation about the system:
I have a table that contains baskets, and I have another table (tbl) that contains items. The basket is created by the application and is assigned an ID from AUTO_INCREMENT on baskets' table. The task is to insert items in basket with id = id_1, into tbl, assigning them a unique ID within that basket's scope. Each item has some data associated with it, which may repeat within the same basket. So in practice, I am trying to store all the data entries within a single basket, and then be able to refer to (and retrieve) these individual entries by their id_1-id_2 pairs.
With your table structure description, it is clear that it does not have a primary key field whose values can be auto generated. MySQL's information_schema.tables does not hold auto_increment value but null for those fields which are not defined auto_increment.
Trigger issue:
The code block used in your trigger body seems depending on explicit calculation and input for the id fields. It did not use the default behaviour of an auto_increment field.
As per MySQL's documentation on LAST_INSERT_ID:
LAST_INSERT_ID() returns a BIGINT UNSIGNED (64-bit) value
representing the first automatically generated value
successfully inserted for an AUTO_INCREMENT column
as a result of the most recently executed INSERT statement.
It is clear that it is for auto_increment fields only.
None of the fields id_1 and id_2 are attributed auto_increment.
Due to the reason, though you pass null as input for those fields while inserting, no value will be auto generated and assigned to them.
Alter your table to set auto_increment to one of those id_x fields, and then start inserting values. One caution is that passing a value explicitly to an auto_increment field during insertion will cause last_insert_id return a zero or most recent auto generated value, but not the NEW.id. Passing a null or not choosing the auto_increment field during insertion will trigger generation of NEW value for that field and last_insert_id can pick and return it.
Following example demonstrates above behaviour:
mysql> drop table if exists so_q27476005;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> create table so_q27476005( i int primary key );
Query OK, 0 rows affected (0.33 sec)
Following statement shows next applicable auto_increment value for a field.
mysql> select auto_increment
-> from information_schema.tables
-> where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
| NULL |
+----------------+
1 row in set (0.00 sec)
Let us try inserting a null value into the field.
mysql> insert into so_q27476005 values( null );
ERROR 1048 (23000): Column 'i' cannot be null
Above statement failed because input was into a not null primary key field but not attributed for auto_increment. Only for auto_increment fields, you can pass null inputs.
Now let us see the behaviour of last_insert_id:
mysql> insert into so_q27476005 values( 1 );
Query OK, 1 row affected (0.04 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 0 |
+------------------+
1 row in set (0.00 sec)
As the input was explicit and also the field is not attributed for auto_increment,
call for last_insert_id resulted a 0. Note that, this can also be some value else,
if there was another insert call for any other auto_increment field of another table,
in the same database connection session.
Let us see the records in the table.
mysql> select * from so_q27476005;
+---+
| i |
+---+
| 1 |
+---+
1 row in set (0.00 sec)
Now, let us apply auto_increment to the field i.
mysql> alter table so_q27476005 change column i i int auto_increment;
Query OK, 1 row affected (0.66 sec)
Records: 1 Duplicates: 0 Warnings: 0
Following statement shows next applicable auto_increment value for the field i.
mysql> select auto_increment
-> from information_schema.tables
-> where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
| 2 |
+----------------+
1 row in set (0.00 sec)
You can cross check that the last_insert_id still is the same.
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 0 |
+------------------+
1 row in set (0.00 sec)
Let us insert a null value into the field i.
mysql> insert into so_q27476005 values( null );
Query OK, 1 row affected (0.03 sec)
It succeeded though passing a null to a primary key field,
because the field is attributed for auto_increment.
Let us see which value was generated and inserted.
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
And the next applicable auto_increment value for the field i is:
mysql> select auto_increment
-> from information_schema.tables
-> where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
| 3 |
+----------------+
1 row in set (0.00 sec)
mysql> select * from so_q27476005;
+---+
| i |
+---+
| 1 |
| 2 |
+---+
2 rows in set (0.00 sec)
Now, let us observe how last_insert_id results when explicit input is given for the field.
mysql> insert into so_q27476005 values( 3 );
Query OK, 1 row affected (0.07 sec)
mysql> select * from so_q27476005;
+---+
| i |
+---+
| 1 |
| 2 |
| 3 |
+---+
3 rows in set (0.00 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
You can see that last_insert_id did not capture the value due to explicit input.
But, information schema do registered next applicable value.
mysql> select auto_increment
-> from information_schema.tables
-> where table_name='so_q27476005';
+----------------+
| auto_increment |
+----------------+
| 4 |
+----------------+
1 row in set (0.08 sec)
Now, let us observe how last_insert_id results when input for the field is auto/implicit.
mysql> insert into so_q27476005 values( null );
Query OK, 1 row affected (0.10 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 4 |
+------------------+
1 row in set (0.00 sec)
Hope, these details help you.

MySQL INSERT ... ON DUPLICATE KEY not updating table with no errors or warnings

So I have the following table:
mysql> show create table user_api_skills \G
*************************** 1. row ***************************
Table: user_api_skills
Create Table: CREATE TABLE `user_api_skills` (
`characterID` int(11) NOT NULL,
`typeID` int(11) NOT NULL,
`level` enum('0','1','2','3','4','5') NOT NULL DEFAULT '0',
`skillpoints` int(11) NOT NULL,
`currentTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`characterID`,`typeID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql>
And in that table a row which I am trying to insert/update:
mysql> SELECT * FROM `user_api_skills` WHERE `characterID` =93192782 AND `typeID` =3359;
+-------------+--------+-------+-------------+---------------------+
| characterID | typeID | level | skillpoints | currentTime |
+-------------+--------+-------+-------------+---------------------+
| 93192782 | 3359 | 3 | 135765 | 2013-09-30 16:58:35 |
+-------------+--------+-------+-------------+---------------------+
1 row in set (0.00 sec)
I believe my query is correctly formed and when executed it doesn't throw any errors or warnings:
mysql> INSERT INTO user_api_skills (characterID,typeID,level,skillpoints)
VALUES (93192782,3359,4,135765) ON DUPLICATE KEY UPDATE level=4,
skillpoints=135765,currentTime=NOW();
Query OK, 2 rows affected (0.22 sec)
I get 2 rows updated (as I would expect from an insert on dup update)
mysql> SELECT * FROM `user_api_skills` WHERE `characterID` =93192782 AND `typeID` =3359;
+-------------+--------+-------+-------------+---------------------+
| characterID | typeID | level | skillpoints | currentTime |
+-------------+--------+-------+-------------+---------------------+
| 93192782 | 3359 | 3 | 135765 | 2013-09-30 16:59:13 |
+-------------+--------+-------+-------------+---------------------+
1 row in set (0.00 sec)
mysql>
but the row itself only changes a single value (the currentTime). Can anybody explain why the other two fields are not updating?
Sorry, I have solved this myself. The level field is an ENUM and the query specified the new value as a number. Updating the query to the following resulted in the expected results.
mysql> INSERT INTO user_api_skills (characterID,typeID,level,skillpoints) VALUES
(93192782,3359,4,135765) ON DUPLICATE KEY UPDATE level='4', skillpoints=135765,
currentTime=NOW();
By providing a int to the update it updated to the one based number choice of the enum, so in this instance, the 4th choice is '3'.

Auto Increment reset to 0, but cannot insert value with id=0. Does not happen for values >0

I've just stumbled on a very weird behaviour:
Imagine we have a table customers:
MariaDB [connections]> describe customers;
+--------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+----------------+
| customerId | int(11) | NO | PRI | NULL | auto_increment |
| customerName | varchar(50) | NO | | NULL | |
+--------------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
Insert a couple of values:
insert into customers(customerName) values('Foo');
insert into customers(customerName) values('Bar');
Then delete everything and reset the auto increment:
DELETE FROM customers;
ALTER TABLE customers AUTO_INCREMENT = 0;
Now, insert a new value with customerId=0:
INSERT INTO customers(customerId,customerName) VALUES(0,'Site owner');
And see the result:
MariaDB [connections]> select * from customers;
+------------+--------------+
| customerId | customerName |
+------------+--------------+
| 1 | Site owner |
+------------+--------------+
1 row in set (0.00 sec)
customerId is set to 1!!!!
Repeat the same procedure but reset to 5 and insert 5, everything is OK:
MariaDB [connections]> delete from customers;
Query OK, 1 row affected (0.00 sec)
MariaDB [connections]> ALTER TABLE customers AUTO_INCREMENT = 5;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [connections]> INSERT INTO customers(customerId,customerName) VALUES(5,'Site owner');
Query OK, 1 row affected (0.00 sec)
MariaDB [connections]> select * from customers;
+------------+--------------+
| customerId | customerName |
+------------+--------------+
| 5 | Site owner |
+------------+--------------+
1 row in set (0.00 sec)
What is going on here? How can I insert the value '0' with insert values? (Yes, I can edit afterwards but for various reasons it is not practical for my case).
Thanks!
I've referred the answer from the link
You can use:
SET [GLOBAL|SESSION] sql_mode='NO_AUTO_VALUE_ON_ZERO'
Which as described here, will prevent MySQL from interpreting an INSERT/UPDATE ID of 0 as being the next sequence ID. Such behaviour will be limited to NULL.
It is what I'd consider pretty bad behaviour from the application though. You'll have to be real careful that it's used consistently, especially if you choose to implement replication at a later date.
This is impossible.
0 is reserved for auto_increment field. When you insert to auto increment row 0 or null then mysql insert record with current auto increment value.

Generating automatic numbers in MySQL

I write a query and give a table in output but I want to every row of my table has a number automatically that starts from 1. for example , I want that my table has "number" column that first row of my table have 1 in that column , second row of my table have 2 in that column , third row of my table have 3 in that column , ...
How can I do this ?
thanks
** My DBMS is MySQL **
Use a variable and increment it like following.
set #num:=0;
select *, #num:=#num+1 `Row` from names;
Example
mysql> create table names( name varchar(10) primary key )engine=Innodb charset=utf8 collate utf8_general_ci;
Query OK, 0 rows affected (0.09 sec)
mysql> insert into names values('a'), ('b'), ('cat'), ('dog'), ('parrot'), ('bird');
Query OK, 6 rows affected (0.04 sec)
Records: 6 Duplicates: 0 Warnings: 0
mysql> set #num:=0; select *, #num:=#num+1 `Row` from names;
Query OK, 0 rows affected (0.00 sec)
+--------+------+
| name | Row |
+--------+------+
| a | 1 |
| b | 2 |
| bird | 3 |
| cat | 4 |
| dog | 5 |
| parrot | 6 |
+--------+------+
6 rows in set (0.00 sec)
Note: If you use * make sure it precedes.
AUTO_INCREMENT is what you are looking for
Like this :
CREATE TABLE animals (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM;
Taken from here: http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
EDIT: Based on the comment someone might want to add that manually. You select the maximum value from your filed, increment by 1 and then insert it into your table.
SELECT MAX(id) FROM animals
Make your primary key an auto increment field.
More info here: http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html