How to backfill an auto increment field in MySQL - mysql

I have a MySQL Database 5.6.32 connected to SuiteCRM. I am using a plugin that allows for the creation of an auto increment field without any coding.
The challenge I'm having is that I have created this auto increment field after records with data are already in our system. I need to update all previous entries with the auto increment values.
When I create a new record the auto increment field works fine, but I need the unique number for all records as this is being used as a Unique Identifier and the default ID in the system is too long for us to use.
The type of auto increment field it created in the MySQL database is as follows:
# Name Type Collation Null Default
10 customer_number_c varchar(80) utf8_general_ci Yes NULL
This is what I have tried so far to try and populate the field:
UPDATE `suitecrm`.`accounts_cstm` SET `customer_number_c` = auto_increment
The result is:
ERROR #1054 - Unknown column 'AUTO_INCNREMENTAL' in 'Field list'
The field already has a default value of NULL in it as well.

MySQL's builtin auto-increment feature only works with columns of integer data types. Yours is varchar(80). I'm not sure why you did that, but I assume it was deliberate.
You could use a session variable to populate the customer number. As you assign values, it will implicitly cast the integer value of the session variable to the string representation.
SET #num := 0;
UPDATE suitecrm.accounts_cstm
SET customer_number_c = (#num := #num + 1)
ORDER BY ...;
You would have to specify some ORDER BY clause to make sure the increasing values get assigned in the order you want them to be.
But you still won't be able to use AUTO_INCREMENT on the customer_number_c column. So your app must generate new customer number values before inserting new rows to this table.

MySQL will retroactively populate existing rows for you if you add an auto_increment primary key. I just validated this with the following test code:
mysql> create table mytable (name varchar(32)) engine=innodb;
Query OK, 0 rows affected (0.04 sec)
mysql> insert into mytable (name) values ('miles'), ('trane'), ('monk');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from mytable;
+-------+
| name |
+-------+
| miles |
| trane |
| monk |
+-------+
3 rows in set (0.00 sec)
mysql> alter table mytable add column id int unsigned primary key auto_increment first;
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> select * from mytable;
+----+-------+
| id | name |
+----+-------+
| 1 | miles |
| 2 | trane |
| 3 | monk |
+----+-------+
3 rows in set (0.00 sec)

Related

MySQL: Cannot update JSON column to convert value from float to integer

I have a MySQL table with a JSON column. I want to update some rows in the JSON column to change a json value from a float to an integer. e.g {"a": 20.0} should become {"a": 20}. It looks like MySQL finds these 2 values equivalent, so it never bothers to update the row.
Here is the state of my test table:
mysql> describe test;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| id | int | NO | PRI | NULL | |
| val | json | YES | | NULL | |
+-------+------+------+-----+---------+-------+
2 rows in set (0.00 sec)
mysql> select * from test;
+----+-------------+
| id | val |
+----+-------------+
| 1 | {"a": 20.0} |
+----+-------------+
1 row in set (0.00 sec)
My aim is to change val to {"a": 20}
I've tried the following queries:
mysql> update test set val=JSON_OBJECT("a", 20) where id=1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
(0 rows changed)
mysql> update test
set val=JSON_SET(
val,
"$.a",
FLOOR(
JSON_EXTRACT(val, "$.a")
)
)
where id=1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
(0 rows changed)
mysql> insert into test (id, val) values (1, JSON_OBJECT("a", 20)) ON DUPLICATE KEY UPDATE id=VALUES(id), val=VALUES(val);
Query OK, 0 rows affected, 2 warnings (0.00 sec)
(0 rows affected)
It looks like it doesn't matter how I try to write it, whether I attempt to modify the existing value, or specify a whole new JSON_OBJECT. So I'm wondering if the reason is simply that MySQL considers the before & after values to be equivalent.
Is there any way around this?
(This does not address the original Question, but addresses a problem encountered in Answering it.)
Gross... 8.0 has a naughty history of all-too-quickly removing something after recently deprecating it. Beware. Here is the issue with VALUES from the Changelog for 8.0.20:
----- 2020-04-27 8.0.20 General Availability -- -- -----
The use of VALUES() to access new row values in INSERT ... ON DUPLICATE KEY UPDATE statements is now deprecated, and is subject to removal in a future MySQL release. Instead, you should use aliases for the new row and its columns as implemented in MySQL 8.0.19 and later.
For example, the statement shown here uses VALUES() to access new row values:
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
Henceforth, you should instead use a statement similar to the following, which uses an alias for the new row:
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new
ON DUPLICATE KEY UPDATE c = new.a+new.b;
Alternatively, you can employ aliases for both the new row and each of its columns, as shown here:
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new(m,n,p)
ON DUPLICATE KEY UPDATE c = m+n;
For more information and examples, see INSERT ... ON DUPLICATE KEY UPDATE Statement.

mysql_insert_id() for INSERT...SELECT statement

I am doing similar INSERT...SELECT query to this
INSERT INTO table (value1, value2)
SELECT 'stuff for value1', 'stuff for value2' FROM DUAL
WHERE NOT EXISTS (SELECT * FROM table
WHERE value1='stuff for value1' AND value2='stuff for value2')
LIMIT 1
, where table has auto-genrated id.
And I would like to know if it was inserted or not, of course. I assume the way to do that is to use mysql_insert_id(). It returns 0 if no insertions happen and 1 if insertions happen. Looking more details here.
If an INSERT ... SELECT statement is executed, and NO automatically
generated value is successfully inserted, mysql_insert_id() RETURNS
the ID of the last inserted row.
What does it return if no auto-generated ID was successfully inserted? Is this a doc typo?
UPDATE1
So far I did testing in C and mysql_insert_id() returns always 0 if insertion did not happen even if the last insertion succeeded and mysql_insert_id() returned non-zero result. A paragraphs in the same manual, mentioned above, confirms this behavior by saying:
mysql_insert_id() returns 0 if the previous statement does not use an AUTO_INCREMENT value. ....
The value of mysql_insert_id() is affected only by statements issued within the current client connection. It is not affected by statements issued by other clients.
The LAST_INSERT_ID() SQL function will contain the value of the first automatically generated value that was successfully inserted. LAST_INSERT_ID() is not reset between statements because the value of that function is maintained in the server. ....
And that feels kind of logical otherwise INSERT...SELECT would be useless in many cases, if you cannot know within the code if your insertion worked or not. But it totally contradicts to the statement above. Did anyone have experience with this?
UPDATE2
From MariaDB manual, also suggests that the value should be zero in case of insertion did not happen:
The mysql_insert_id() function returns the ID generated by a query on
a table with a column having the AUTO_INCREMENT attribute or the value
for the last usage of LAST_INSERT_ID(expr). If the last query wasn't
an INSERT or UPDATE statement or if the modified table does not have a
column with the AUTO_INCREMENT attribute and LAST_INSERT_ID was not
used, this function will return zero.
The wording could be more clear, but what it means is that if your INSERT causes an error, mysql_insert_id() (or the SQL function last_insert_id()) continues to report whatever it did based on an earlier successful INSERT.
Here's a demo:
mysql> create table foo( id int auto_increment primary key);
mysql> create table bar( id int primary key);
mysql> insert into bar (id) values (1), (2), (10);
mysql> insert into foo select id from bar;
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 0 |
+------------------+
No new auto-inc values were generated, because my INSERT gave specific values to insert.
Let's generate some new values:
mysql> insert into foo select null from bar;
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 11 |
+------------------+
This is expected, because last_insert_id() will report the first id generated by a batch insert. You have to do the math to figure out how many rows were inserted, so you can know the rest of the id's. The id's generated in this way are guaranteed to be unique and consecutive.
Now let's try inserting some duplicates, which will cause an error:
mysql> insert into foo select id from bar;
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
Now comes the point of the sentence in the documentation: there has been no change in what last_insert_id() reports.
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 11 |
+------------------+
Likewise, even if the INSERTs are successful, but do not cause any new auto-inc values to be generated, there is no change in what last_insert_id() reports.
mysql> insert into foo select id+20 from bar;
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 11 |
+------------------+
Many people assume last_insert_id() reports the most recent primary key value inserted, but it doesn't. It only reports values that were generated automatically by the auto-inc feature.
mysql_affected_rows is your friend. It will be greater than 0, if you successfully inserted rows (except when it returns (my_ulonglong)-1, which indicates failure). In your case, since you insert at most 1 row, you just need to check whether it returned 1.
It looks like it will return the id that was last auto-generated:
MariaDB [stackoverflow]> desc a;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| a | varchar(20) | YES | | NULL | |
| b | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)
MariaDB [stackoverflow]> insert into a(a,b) values('haha', 'haha');
Query OK, 1 row affected (0.03 sec)
MariaDB [stackoverflow]> select LAST_INSERT_ID() from dual;
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 1 |
+------------------+
MariaDB [stackoverflow]> insert into a(a,b) select 'hi', 'hello' from dual;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
MariaDB [stackoverflow]> select LAST_INSERT_ID() from dual;
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
MariaDB [stackoverflow]> insert into a(a,b) select 'hi', 'hello' from dual where not exists (select * from a where a='hi' and b='hello') limit 1;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [stackoverflow]> select LAST_INSERT_ID() from dual;
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)

Save UUID as varbinary(16) in MySQL causes com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'ID' at row 1

I have a field id defined as below. It's varbinary(16) in database, when i am inserting a new record through JPA, i got "com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'ID' at row 1". What am I doing wrong?
#Id
#Column(name="ID")
private UUID id;
A UUID is a 128-bit number represented by a utf8 string of five hexadecimal numbers separated by hyphen( '-' ). Char length of the string returned by UUID() is '36'.
Hence column definition with '16' length is not sufficient. And when defined so, you will receive the said error.
mysql> create table tbl_so_q24028471_vb( v varbinary(16) );
Query OK, 0 rows affected (0.42 sec)
mysql> desc tbl_so_q24028471_vb;
+-------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------------+------+-----+---------+-------+
| v | varbinary(16) | YES | | NULL | |
+-------+---------------+------+-----+---------+-------+
1 row in set (0.17 sec)
mysql> insert into tbl_so_q24028471_vb values( uuid() );
ERROR 1406 (22001): Data too long for column 'v' at row 1
Change the column definition to accommodate more length and use.
mysql> alter table tbl_so_q24028471_vb modify column v varbinary(36);
Query OK, 0 rows affected (0.86 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> insert into tbl_so_q24028471_vb values( uuid() );
Query OK, 1 row affected (0.08 sec)
mysql> select * from tbl_so_q24028471_vb;
+--------------------------------------+
| v |
+--------------------------------------+
| 630d3270-ebba-11e3-bd03-bc8556a95cc2 |
+--------------------------------------+
Your trying to store a 36 character string in a 16 byte space. That's not going to work.
You need get at the bits behind the UUID, which will fit in a binary(16). Since you're using Java, you can do something like:
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
and then insert the resulting byte array into the DB.

How to program a MySQL trigger to insert row into another table?

I'm looking to create a MySQL trigger on a table. Essentially, I'm creating an activity stream and need to log actions by users. When a user makes a comment, I want a database trigger on that table to fire and:
Grab the ID of the last inserted row (the id of the comment row).
perform an INSERT into an activities table, using data from the last inserted row.
I'll essentially replicate this trigger for deleting comments.
Questions I had:
Is LAST_INSERT_ID() the best way to grab the id?
How do I properly store the data from the last inserted comment row for use in my "INSERT into activities" statement?
Should I be using a combination of stored procedures as well as the trigger?
What would the basic structure of the trigger look like?
Thanks! It's been a few years since I've touched anything to do with DB triggers, procedures and functions.
drop table if exists comments;
create table comments
(
comment_id int unsigned not null auto_increment primary key,
user_id int unsigned not null
)
engine=innodb;
drop table if exists activities;
create table activities
(
activity_id int unsigned not null auto_increment primary key,
comment_id int unsigned not null,
user_id int unsigned not null
)
engine=innodb;
delimiter #
create trigger comments_after_ins_trig after insert on comments
for each row
begin
insert into activities (comment_id, user_id) values (new.comment_id, new.user_id);
end#
delimiter ;
insert into comments (user_id) values (1),(2);
select * from comments;
select * from activities;
Edit:
mysql> \. d:\foo.sql
Database changed
Query OK, 0 rows affected (0.10 sec)
Query OK, 0 rows affected (0.30 sec)
Query OK, 0 rows affected (0.11 sec)
Query OK, 0 rows affected (0.35 sec)
Query OK, 0 rows affected (0.07 sec)
Query OK, 2 rows affected (0.03 sec)
Records: 2 Duplicates: 0 Warnings: 0
+------------+---------+
| comment_id | user_id |
+------------+---------+
| 1 | 1 |
| 2 | 2 |
+------------+---------+
2 rows in set (0.00 sec)
+-------------+------------+---------+
| activity_id | comment_id | user_id |
+-------------+------------+---------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
+-------------+------------+---------+
2 rows in set (0.00 sec)

Mysql 4 vs Mysql 5 auto-increment field on insert

I've learned this along the way but can't figure out where I read it or heard it, as there is nothing I have found online supporting it, but I remember that when upgrading from mysql4.x to mysql5.x, one of the required changes was that the auto-increment field for inserts had to change from '' to NULL if it was included.
I know its not required to have in the insert anyway, but just for point of interest...
Mysql 4.x would allow:
INSERT INTO TABLE (table_id, name, location) VALUES ('', 'john', 'NY');
But mysql 5.x had to have:
INSERT INTO TABLE (table_id, name, location) VALUES (NULL, 'john', 'NY');
I can't find any information on mysql's site to support this, but I know for a fact it throws an error in mysql 5.x and know it worked with '' in 4.x, but where is this documented?
Both the 4.1 and 5.0 docs state that 0 or NULL is required:
No value was specified for the
AUTO_INCREMENT column, so MySQL
assigned sequence numbers
automatically. You can also explicitly
assign NULL or 0 to the column to
generate sequence numbers.
It does not matter, mysql internally still convert to integer
mysql> CREATE TABLE some_test ( id int(10) unsigned NOT NULL auto_increment, primary key(id));
Query OK, 0 rows affected (0.00 sec)
mysql> insert into some_test values ('');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+------------------------------------------------------+
| Level | Code | Message |
+---------+------+------------------------------------------------------+
| Warning | 1264 | Out of range value adjusted for column 'id' at row 1 |
+---------+------+------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select * from some_test;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)
However, I will suggest use 0 to avoid this warning