MySQL: Manually increment a varchar for one insert statement - mysql

I am converting data from one database to another. The target db has a table called provider with primary key provider_no varchar(6).
I'm writing an insert to copy from source table to target table, and need an incremented key for provider_no. Is there a function to return even the iterations for one insert statement?
There are a lot more columns, but the basic problem i'm trying to solve is:
INSERT INTO `target`.`provider`
(`provider_no`,
`lastUpdateDate`)
SELECT
'', --incremented value
now()
from `source`.`provider`;
Auto Increment only works for int values, but i'm not at liberty here to change the data type.
Also, the source table doesn't have a usable primary key value that I can use for this copy.

To increment the varchar, first cast it to a number (either signed, or unsigned) like so:
INSERT INTO `target`.`provider`
(`provider_no`,
`lastUpdateDate`)
SELECT
cast(the_varchar_field as signed) + 1, --incremented value
now()
from `source`.`provider`;
Example:
mysql> select cast("001" as unsigned) + 1;
+-----------------------------+
| cast("001" as unsigned) + 1 |
+-----------------------------+
| 2 |
+-----------------------------+
Sorry, i thought you wanted to increment the varchar field from the source table.
To 'emulate' an auto increment field as you want to do, we can do it with variables like this:
insert into provider
select #cnt := #cnt +1, now()
from sourceprovider, (select #cnt := 0) q;
And here's a little demo: http://sqlfiddle.com/#!9/09fd4/1

Here's a solution I just implemented. There's a unique column called did on the source table, which I can use indirectly. There's probably a more elegant way to do it, but:
create table temp_key (
key_id int auto_increment primary key,
did varchar(4));
insert into temp_key (did) select did from source.provider;
INSERT INTO target.provider
(provider_no,
lastUpdateDate)
SELECT
k.key_id,
now()
FROM source.provider d
JOIN temp_key k on d.did = k.did;
drop table temp_key;

Related

Is there a way to insert an auto-incremental primary id with a prefix in mysql database?

I'm trying to insert a data as a primary ID that has one alphanumerical value and two numerical value in MySQL database. This data will auto incrementally generate number, but the alphanumerical value will be fixed. Like, D1, D2....D54, D55, D56 etc. Here, 'D' is always the same, but the number will be automatically incremented. Is there any way to do this?
First of all it's unadvisable to do so, like others commented, you can have this id value generated on the fly.
But if nonetheless you want it your way there're at least two ways to do so:
More or less reliable way involves using a separate table for sequencing and a trigger
Schema:
CREATE TABLE Table1_seq
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE Table1
(
`id` VARCHAR(10) NOT NULL PRIMARY KEY DEFAULT '',
...
);
Trigger:
DELIMITER $$
CREATE TRIGGER tg_bi_table1
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
INSERT INTO table1_seq() VALUES();
SET NEW.id = CONCAT('D', LPAD(LAST_INSERT_ID(), 4,'0'));
END$$
DELIMITER ;
Then you just insert your rows to table1
INSERT INTO Table1 () VALUES (),(),();
And you'll get
| ID |
---------
| D0001 |
| D0002 |
| D0003 |
Here is SQLFiddle demo
Unreliable way is to generate your new id on the fly in INSERT statement itself
INSERT INTO Table1 (id, ...)
SELECT CONCAT('D', LPAD(COALESCE(SUBSTR(MAX(id), 2), 0) + 1, 4, '0')),
...
FROM table1
Here is SQLFiddle demo
The problems with this approach:
Under heavy load two concurrent sessions can grab the same MAX(id) value and therefore generate the same new id leading to the failure of insert.
You can't use multi-insert statements
We can't set auto-increment for alphanumeric. In your case if D is always same then no need to add it to your pk field. Keep your constant in a separate field and add it when you select.

mysql insert with value equal to primary key + 1

I have an auto increment column ID, and for some situation I wanted the other column to be equal to the primary key + 1 value
ID | other
1 | 2
2 | 3
3 | 4
4 | 123 (some situation, it is not always plus 1)
How can I achieve this?
Here's what I have tried
INSERT INTO table (`ID`,`other`) VALUES ('',(SELECT MAX(ID)+1 FROM table))
But that returns an error
You can't specify target table 'table' for update in FROM clause
Try Below query:
ALTER TABLE dbo.table ADD
Column AS ([ID]+1)
GO
It will definitely work
Using a normal AUTO_INCREMENT column as id, I cannot think of a way to do this in MySQL. Triggers, which otherwise would have been an option, don't work well with AUTO_INCREMENT columns.
The only way I see is to do two commands for an INSERT;
INSERT INTO bop (value) VALUES ('These values should be 1 and 2');
UPDATE bop SET other = id+1 WHERE id = LAST_INSERT_ID();
An SQLfiddle to test with.
The closest I'm getting to what you're looking for is to generate sequences separately from AUTO_INCREMENT using a function, and use that instead to generate the table id;
DELIMITER //
CREATE TABLE bop (
id INT UNIQUE,
other INT,
value VARCHAR(64)
)//
CREATE TABLE bop_seq ( seq INT ) // -- Sequence table
INSERT INTO bop_seq VALUES (1) // -- Start value
CREATE FUNCTION bop_nextval() RETURNS int
BEGIN
SET #tmp = (SELECT seq FROM bop_seq FOR UPDATE);
UPDATE bop_seq SET seq = seq + 1;
RETURN #tmp;
END//
CREATE TRIGGER bop_auto BEFORE INSERT ON bop
FOR EACH ROW
SET NEW.id = bop_nextval(), NEW.other=NEW.id + 1;
//
That'd let you do inserts and have it autonumber like you want. The FOR UPDATE should keep the sequence transaction safe, but I've not load tested so you may want to do that.
Another SQLfiddle.
I solved this by updating 2 times the DB..
I wanted to do +1 from 19 till ..
UPDATE `table` SET `id`=`id`+101 WHERE id <= 19
UPDATE `table` SET `id`=`id`-100 WHERE id <= 119 AND id >= 101

using mysql to auto populate a sort order

I have a table called t_home_feature with the following columns: id, type, sort_order. I then executed the following MySQL statement:
INSERT INTO t_home_feature (SELECT news_id, 'news', ( SELECT max(sort_order) + 1 FROM t_home_feature ) FROM t_news )
I then did a SELECT * FROM t_home_feature but the sort_order for all rows has a value that is equal to the number of rows in t_home_feature prior to the insert statement, instead of a value like previous row + 1.
How can I modify my insert query to achieve a previous row + 1 output?
You could turn the sort_order into an auto_increment field, which means the database will automatically increment it and you need not refer to it in your insert. This has to be a key, but not a primary key. For example, here's an example from:
http://forums.mysql.com/read.php?22,264498,264967#msg-264967
There the link has an example of a workaround:
create table ai (
id int auto_increment not null,
xx varchar(9),
key(id),
primary key (xx));
You may have to do something fancy with local variables. Perhaps something like this:
SELECT COUNT(1) INTO #maxval FROM t_home_feature;
INSERT INTO t_home_feature
SELECT news_id, 'news', #maxval:=#maxval+1 FROM t_news ;
No need to any auto increment values from tables.
I have done things like this when answering questions before. Here are four(4) examples of questions I answered in the DBA StackExchange using local variables:
https://dba.stackexchange.com/questions/29007/update-ranking-on-table/29009#29009
https://dba.stackexchange.com/questions/29016/selecting-without-repititions/29018#29018
https://dba.stackexchange.com/questions/18987/update-rank-on-a-large-table/18990#18990
https://dba.stackexchange.com/questions/10251/whats-wrong-with-this-update-rank-query10320#10320

adding '1' returns "BLOB"

A JobID goes as follows: ALC-YYYYMMDD-001. The first three are a companies initials, the last three are an incrementing number that resets daily and increments throughout the day as jobs are added for a maximum of 999 jobs in a day; it is these last three that I am trying to work with.
I am trying to get a before-insert trigger to look for the max JobID of the day, and add one so I can have the trigger derive the proper JobID. For the first job, it will of course return null. So here is what I have so far.
Through the following I can get a result of '000'.
set #maxjobID =
(select SUBSTRING(
(Select MAX(
SUBSTRING((Select JobID FROM jobs WHERE SUBSTRING(JobID,5,8)=date_format(curdate(), '%Y%m%d')),4,12)
)
),14,3)
);
select lpad((select ifnull(#maxjobID,0)),3,'0')
But I really need to add one to this keeping the leading zeros to increment the first and subsequent jobs of the day. My problem is as soon as try to add '1' I get a return of 'BLOB'. That is:
select lpad((select ifnull(#maxjobID,0)+1),3,'0')
returns 'BLOB'
I need it to return '001' so I can concatenate that result with the CO initials and the current date.
try casting VARCHAR back to INTEGER
SELECT lpad(SELECT (COALESCE(#maxjobID,0, CAST(#maxjobID AS SIGNED)) + 1),3,'0')
If you're using the MyISAM storage engine, you can implement exactly this with AUTO_INCREMENT, without denormalising your data into a delimited string:
For MyISAM tables, you can specify AUTO_INCREMENT on a secondary column in a multiple-column index. In this case, the generated value for the AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1 WHERE prefix=given-prefix. This is useful when you want to put data into ordered groups.
In your case:
Normalise your schema:
ALTER TABLE jobs
ADD initials CHAR(3) NOT NULL FIRST,
ADD date DATE NOT NULL AFTER initials,
ADD seq SMALLINT(3) UNSIGNED NOT NULL AFTER date,
;
Normalise your existing data:
UPDATE jobs SET
initials = SUBSTRING_INDEX(JobID, '-', 1),
date = STR_TO_DATE(SUBSTRING(JobID, 5, 8), '%Y%m%d'),
seq = SUBSTRING_INDEX(JobID, '-', -1)
;
Set up the AUTO_INCREMENT:
ALTER TABLE jobs
DROP PRIMARY KEY,
DROP JobID,
MODIFY seq SMALLINT(3) UNSIGNED NOT NULL AUTO_INCREMENT,
ADD PRIMARY KEY(initials, date, seq)
;
You can then recreate your JobID as required on SELECT (or even create a view from such a query):
SELECT CONCAT_WS(
'-',
initials,
DATE_FORMAT(date, '%Y%m%d'),
LPAD(seq, 3, '0')
) AS JobID,
-- etc.
If you're using InnoDB, whilst you can't generate sequence numbers in this fashion I'd still recommend normalising your data as above.
So, I found a query that works (thus far).
Declare maxjobID VARCHAR(16);
Declare jobincrement SMALLINT;
SET maxjobID =
(Select MAX(
ifnull(SUBSTRING(
(Select JobID FROM jobs WHERE SUBSTRING(JobID,5,8)=date_format(curdate(), '%Y%m%d')),
5,
12),0)
)
);
if maxjobID=0
then set jobincrement=1;
else set jobincrement=(select substring(maxjobID,10,3))+1;
end if;
Set NEW.JobID=concat
(New.AssignedCompany,'-',date_format(curdate(), '%Y%m%d'),'-',(select lpad(jobincrement,3,'0')));
Thanks for the responses! Especially eggyal for pointing out the auto_increment capabilities in MyISAM.

Assigning row rank numbers

I have a database. I want to update a column of it. The column should contain unique integer numbers in ascending order according to alphabetical order of another column.
Sorry not clear maybe, I want to have integer numbers like this:
1 ACC 501
2 BCC 501
3 GCC 601
4 FCC 601
Is there a reasonably simple way of setting this rank/order with mysql or sql query?
What you need is a ranking function which is not supported by MySQL at the moment. However, you can simulate them like so:
Set #rownum := 0;
Select rnk, SomeCode, SomeNum
From (
Select #rownum := #rownum + 1 As rnk, SomeCode, SomeNum
From MyTable
Order By SomeCode Asc
) As Z
Create another table that has the same schema as your original table, plus the new column. The new column should be an autonumber. Do an INSERT...SELECT into that table. The new column will be filled out with the values you want.
Like what Alex said, you want to create a new table like
CREATE TABLE newTable(
#Table definition from current table,
id INT NOT NULL AUTO_INCREMENT
);
And then insert with
INSERT INTO newTable
SELECT * FROM oldTable
ORDER BY orderColumn;
I think you can quickly do the create table with
CREATE TABLE newTable LIKE oldTable;
ALTER TABLE newTable ADD COLUMN id INT NOT NULL AUTO_INCREMENT;