Related
I have table like this
table
id Varchar(45) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name CHAR(30) NOT NULL,
I want to increment my id field like 'LHPL001','LHPL002','LHPL003'... etc.
What should I have to do for that? Please let me know any possible way.
If you really need this you can achieve your goal with help of separate table for sequencing (if you don't mind) and a trigger.
Tables
CREATE TABLE table1_seq
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE table1
(
id VARCHAR(7) NOT NULL PRIMARY KEY DEFAULT '0', name VARCHAR(30)
);
Now the trigger
DELIMITER $$
CREATE TRIGGER tg_table1_insert
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
INSERT INTO table1_seq VALUES (NULL);
SET NEW.id = CONCAT('LHPL', LPAD(LAST_INSERT_ID(), 3, '0'));
END$$
DELIMITER ;
Then you just insert rows to table1
INSERT INTO Table1 (name)
VALUES ('Jhon'), ('Mark');
And you'll have
| ID | NAME |
------------------
| LHPL001 | Jhon |
| LHPL002 | Mark |
Here is SQLFiddle demo
Create a table with a normal numeric auto_increment ID, but either define it with ZEROFILL, or use LPAD to add zeroes when selecting. Then CONCAT the values to get your intended behavior. Example #1:
create table so (
id int(3) unsigned zerofill not null auto_increment primary key,
name varchar(30) not null
);
insert into so set name = 'John';
insert into so set name = 'Mark';
select concat('LHPL', id) as id, name from so;
+---------+------+
| id | name |
+---------+------+
| LHPL001 | John |
| LHPL002 | Mark |
+---------+------+
Example #2:
create table so (
id int unsigned not null auto_increment primary key,
name varchar(30) not null
);
insert into so set name = 'John';
insert into so set name = 'Mark';
select concat('LHPL', LPAD(id, 3, 0)) as id, name from so;
+---------+------+
| id | name |
+---------+------+
| LHPL001 | John |
| LHPL002 | Mark |
+---------+------+
I know it is late but I just want to share on what I have done for this. I'm not allowed to add another table or trigger so I need to generate it in a single query upon insert. For your case, can you try this query.
CREATE TABLE YOURTABLE(
IDNUMBER VARCHAR(7) NOT NULL PRIMARY KEY,
ENAME VARCHAR(30) not null
);
Perform a select and use this select query and save to the parameter #IDNUMBER
(SELECT IFNULL
(CONCAT('LHPL',LPAD(
(SUBSTRING_INDEX
(MAX(`IDNUMBER`), 'LHPL',-1) + 1), 5, '0')), 'LHPL001')
AS 'IDNUMBER' FROM YOURTABLE ORDER BY `IDNUMBER` ASC)
And then Insert query will be :
INSERT INTO YOURTABLE(IDNUMBER, ENAME) VALUES
(#IDNUMBER, 'EMPLOYEE NAME');
The result will be the same as the other answer but the difference is, you will not need to create another table or trigger. I hope that I can help someone that have a same case as mine.
Here is PostgreSQL example without trigger if someone need it on PostgreSQL:
CREATE SEQUENCE messages_seq;
CREATE TABLE IF NOT EXISTS messages (
id CHAR(20) NOT NULL DEFAULT ('message_' || nextval('messages_seq')),
name CHAR(30) NOT NULL,
);
ALTER SEQUENCE messages_seq OWNED BY messages.id;
quite a few hours struggling with a problem, it's more about the issue.
Depicting the script when you type in textboxa searches in SQL data records where name = contain. Assuming that I entered 2 names separated by a comma (X, Y) 2:
a) If you choose "all containing typed values" is to look for all the records, in the above "name". It works:
$where. = "AND name IN ('". implode (' ', ' ', $array). "')";
Model: Contains X.
Contains The Y;
Contains the Y and X;
It works as it should.
(b)) If you choose "all containing only the values to be entered to look for records in which only occurs in the" name "is what we have.
Here I do not know how to do it.
Model: contains the X and Y-only, I don't want records that contain only X, but Y and Alternatively, if in MySQL "name" is 3 options (X, Y, Z).
I do not know how to explain more clearly:--------------------example: I have 5 values in the table, where the name is in different combinations:
1) Name1, Name2,
2) name1, Name8,
3) name1, Name9,
4) Name8, Name3,
5) Name4, Name5,
using the selected the first option, where the textbox typed: "Name1, Name" the result will be: 1), 2), 3), 4), this is valid.
Using the second result should only be 2).
The result of a) res1 or res2 or res1 and res2.
Now I need somehow to come to a solution, when I type in textbox "res1, res2" the result will be: only the records that contain res1 and res2, and NOTHING AFTER that.
---- EDIT FOR REPLIES:
+--------------------- my_val_search -----------+
| aid | int(11) | primary key auto_increment|
|name | varchar(255)| |
+-----------------------------------------------+
Records:
1|Ruby
2|CSS
3|HTML
4|PHP
5|Python
6|SQL
7|Javascript
8|C++
9|AJAX
10|Java
+---------------------- topics -----------------+
|id | int(11) | Primary key auto_increment |
|dateline| date | |
|author | varchar(30) | |
|message | text | |
|aid |int(11) |foreign key with my_val_search|
+-----------------------------------------------+
Now if you type in the search "Ruby, Python" I want to have found records only for topics where are "Ruby and Python".
By using the
$myValInput = $_GET['keywords'];
$where = "AND a.name IN('" . implode("','", $myValInput. "')";
Searches for records, where when you type "Python, Ruby" is divided into:
-Python
-Ruby,
- Python and Ruby
and I want to make the results appear only for "Python and Ruby". In addition, if someone types "Python, Ruby, Java," the result should be
-Python, Ruby, Java,
the record should be just what I wrote, nothing more.
Using IN cannot get what you expect!
When used with a subquery, the word IN is an alias for = ANY. Thus, these two statements are the same:
SELECT s1 FROM t1 WHERE s1 = ANY (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 IN (SELECT s1 FROM t2);
https://dev.mysql.com/doc/refman/5.7/en/any-in-some-subqueries.html
Besides, I think your schema is incorrect. The topic should be parent and the topic keywords should be child. If I were you, I will do it like this.
CREATE TABLE IF NOT EXISTS `topic` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dateline` date,
`author` varchar(30),
`message` text,
PRIMARY KEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE IF NOT EXISTS `topic_keywords` (
`topic` int(10) unsigned NOT NULL,
`keyword` varchar(255),
FOREIGN KEY (`topic`) REFERENCES `topic` (`id`),
UNIQUE INDEX (`topic`, `keyword`)
) ENGINE=InnoDB;
In your case, I think the full text is suitable.
https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
CREATE TABLE IF NOT EXISTS `topic` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dateline` date,
`author` varchar(30),
`message` text,
`keywords` varchar(255),
PRIMARY KEY(`id`),
FULLTEXT (`keywords`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
INSERT INTO `topic` (`keywords`)
VALUES ('Ruby,Python'),
('Ruby'),
('Ruby,SQL'),
('Python'),
('Python,SQL'),
('Ruby,Python,SQL,AJAX');
SELECT `keywords`
FROM `topic`
WHERE MATCH(`keywords`) AGAINST('+Ruby +Python' IN BOOLEAN MODE);
+----------------------+
| keywords |
+----------------------+
| Ruby,Python |
| Ruby,Python,SQL,AJAX |
+----------------------+
2 rows in set (0.00 sec)
If you just find out the records just exact your input. You can add a hash column. Like design a hash table for your Mysql table. So you can use hash value in your query statement. But don't forget to update the hash value, if your keywords value had been changed. Add the before update trigger might be a good choice.
CREATE TABLE IF NOT EXISTS `topic` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dateline` date,
`author` varchar(30),
`message` text,
`keywords` varchar(255),
`keywords_hash` varchar(32),
PRIMARY KEY(`id`),
FULLTEXT (`keywords`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
INSERT INTO `topic`
(`keywords` , `keywords_hash`)
VALUES ('Ruby,Python', md5('Ruby,Python'));
SELECT `keywords`
FROM `topic`
WHERE `keywords_hash` = md5('Ruby,Python');
Here's an attempt at a solution, but there's better solutions query wise than using a regular denormalized structure to handle this - so if you're going to query this a lot (and with a large number of terms), you might want to look into caching the lookup values in your original table and then querying by their content. Anyway, to solve it with regular joins:
Given data:
CREATE TABLE foo (id int);
CREATE TABLE foo_bar (foo_id int, bar_id int);
CREATE TABLE bar (id int, name varchar(25));
INSERT INTO foo VALUES (1), (2), (3), (4);
INSERT INTO bar VALUES (1, 'Ruby'), (2, 'Python'), (3, 'PHP');
INSERT INTO foo_bar VALUES (1, 1), (2, 1), (3, 3), (4, 3), (2,2);
You can retrieve id values from foo that has both Ruby and Python by joining each term manually:
SELECT f.id FROM foo f
JOIN foo_bar fb ON fb.foo_id = f.id
JOIN bar b ON fb.bar_id = b.id AND b.name = 'Ruby'
JOIN foo_bar fb2 ON fb2.foo_id = f.id
JOIN bar b2 ON fb2.bar_id = b2.id AND b2.name = 'Python'
This gives 2 as the only id that haves all languages present.
This question already has answers here:
How can I do 'insert if not exists' in MySQL?
(11 answers)
Closed 2 years ago.
I am trying to execute the following query:
INSERT INTO table_listnames (name, address, tele)
VALUES ('Rupert', 'Somewhere', '022')
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name='value'
);
But this returns an error. Basically I don't want to insert a record if the 'name' field of the record already exists in another record - how to check if the new name is unique?
I'm not actually suggesting that you do this, as the UNIQUE index as suggested by Piskvor and others is a far better way to do it, but you can actually do what you were attempting:
CREATE TABLE `table_listnames` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`address` varchar(255) NOT NULL,
`tele` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Insert a record:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
+----+--------+-----------+------+
Try to insert the same record again:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
+----+--------+-----------+------+
Insert a different record:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'John'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+--------+-----------+------+
| id | name | address | tele |
+----+--------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
| 2 | John | Doe | 022 |
+----+--------+-----------+------+
And so on...
Update:
To prevent #1060 - Duplicate column name error in case two values may equal, you must name the columns of the inner SELECT:
INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Unknown' AS name, 'Unknown' AS address, '022' AS tele) AS tmp
WHERE NOT EXISTS (
SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
SELECT * FROM `table_listnames`;
+----+---------+-----------+------+
| id | name | address | tele |
+----+---------+-----------+------+
| 1 | Rupert | Somewhere | 022 |
| 2 | John | Doe | 022 |
| 3 | Unknown | Unknown | 022 |
+----+---------+-----------+------+
INSERT doesn't allow WHERE in the syntax.
What you can do: create a UNIQUE INDEX on the field which should be unique (name), then use either:
normal INSERT (and handle the error if the name already exists)
INSERT IGNORE (which will fail silently cause a warning (instead of error) if name already exists)
INSERT ... ON DUPLICATE KEY UPDATE (which will execute the UPDATE at the end if name already exists, see documentation)
Worked :
INSERT INTO users (full_name, login, password)
SELECT 'Mahbub Tito','tito',SHA1('12345') FROM DUAL
WHERE NOT EXISTS
(SELECT login FROM users WHERE login='tito');
MySQL provides a very cute solution :
REPLACE INTO `table` VALUES (5, 'John', 'Doe', SHA1('password'));
Very easy to use since you have declared a unique primary key (here with value 5).
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;
Here the mysql query, that insert records if not exist and will ignore existing similar records.
----Untested----
You can easily use the following way :
INSERT INTO ... ON DUPLICATE KEY UPDATE ...
In this way, you can insert any new raw and if you have duplicate data, replace a specific column ( The best columns are timestamps ).
For example :
CREATE TABLE IF NOT EXISTS Devices (
id INT(6) NOT NULL AUTO_INCREMENT,
unique_id VARCHAR(100) NOT NULL PRIMARY KEY,
created_at VARCHAR(100) NOT NULL,
UNIQUE KEY unique_id (unique_id),
UNIQUE KEY id (id)
)
CHARACTER SET utf8
COLLATE utf8_unicode_ci;
INSERT INTO Devices(unique_id, time)
VALUES('$device_id', '$current_time')
ON DUPLICATE KEY UPDATE time = '$current_time';
To overcome a similar problem, I have modified the table to have a unique column. Using your example, on creation I would have something like:
name VARCHAR(20),
UNIQUE (name)
and then use the following query when inserting into it:
INSERT IGNORE INTO train
set table_listnames='Rupert'
If you really can't get a unique index on the table, you could try...
INSERT INTO table_listnames (name, address, tele)
SELECT 'Rupert', 'Somewhere', '022'
FROM some_other_table
WHERE NOT EXISTS (SELECT name
FROM table_listnames
WHERE name='Rupert')
LIMIT 1;
This query works well:
INSERT INTO `user` ( `username` , `password` )
SELECT * FROM (SELECT 'ersks', md5( 'Nepal' )) AS tmp
WHERE NOT EXISTS (SELECT `username` FROM `user` WHERE `username` = 'ersks'
AND `password` = md5( 'Nepal' )) LIMIT 1
And you can create the table using following query:
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`password` varchar(32) NOT NULL,
`status` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Note: Create table using second query before trying to use first query.
Brian Hooper :
You almost hit the point but you have an error in your synatx. Your insert will never work. I tested on my database and here is the right answer:
INSERT INTO podatki (datum,ura,meritev1,meritev1_sunki,impulzi1,meritev2,meritev2_sunki,impulzi2)
SELECT '$datum', '$ura', '$meritve1','$sunki1','$impulzi1','$meritve2','$sunki2','$impulzi2'
FROM dual
WHERE NOT EXISTS (SELECT datum,ura
FROM podatki
WHERE datum='$datum' and ura='$ura'
I'm giving you my example of y table. Insert is almost the same like Bian Hooper wrote, except that I put the select FROM DUAL ont from other table.
Cind regards, Ivan
This is not an answer, it's just a note. The query like the one in the accepted answer does not work if the inserted values are duplicates, like here:
INSERT INTO `addr` (`email`, `name`)
SELECT * FROM (SELECT 'user#domain.tld', 'user#domain.tld') AS tmp
WHERE NOT EXISTS (
SELECT `email` FROM `addr` WHERE `email` LIKE 'user#domain.tld'
);
Error
SQL query: Copy Documentation
MySQL said: Documentation
#1060 - Duplicate column name 'user#domain.tld'
In the contrary, the query like the one from Mahbub Tito's answer works fine:
INSERT INTO `addr` (`email`, `name`)
SELECT 'user#domain.tld', 'user#domain.tld'
WHERE NOT EXISTS (
SELECT `email` FROM `addr` WHERE `email` LIKE 'user#domain.tld'
);
1 row inserted.
Tested in MariaDB
insert into customer_keyskill(customerID, keySkillID)
select 2,1 from dual
where not exists (
select customerID from customer_keyskill
where customerID = 2
and keySkillID = 1 )
You are inserting not Updating the result.
You can define the name column in primary column or set it is unique.
I had a problem, and the method Mike advised worked partly, I had an error Dublicate Column name = '0', and changed the syntax of your query as this`
$tQ = "INSERT INTO names (name_id, surname_id, sum, sum2, sum3,sum4,sum5)
SELECT '$name', '$surname', '$sum', '$sum2', '$sum3','$sum4','$sum5'
FROM DUAL
WHERE NOT EXISTS (
SELECT sum FROM names WHERE name_id = '$name'
AND surname_id = '$surname') LIMIT 1;";
The problem was with column names. sum3 was equal to sum4 and mysql throwed dublicate column names, and I wrote the code in this syntax and it worked perfectly,
I had a similar problem and I needed to insert multiple if not existing. So from the examples above I came to this combination... it's here just in case somebody would need it.
Notice:
I had to define name everywhere as MSSQL required it... MySQL works with * too.
INSERT INTO names (name)
SELECT name
FROM
(
SELECT name
FROM
(
SELECT 'Test 4' as name
) AS tmp_single
WHERE NOT EXISTS
(
SELECT name FROM names WHERE name = 'Test 4'
)
UNION ALL
SELECT name
FROM
(
SELECT 'Test 5' as name
) AS tmp_single
WHERE NOT EXISTS
(
SELECT name FROM names WHERE name = 'Test 5'
)
) tmp_all;
MySQL:
CREATE TABLE names (
OID int(11) NOT NULL AUTO_INCREMENT,
name varchar(32) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (OID),
UNIQUE KEY name_UNIQUE (name)
) ENGINE=InnoDB AUTO_INCREMENT=1;
or
MSSQL:
CREATE TABLE [names] (
[OID] INT IDENTITY (1, 1) NOT NULL,
[name] NVARCHAR (32) NOT NULL,
PRIMARY KEY CLUSTERED ([OID] ASC)
);
CREATE UNIQUE NONCLUSTERED INDEX [Index_Names_Name] ON [names]([name] ASC);
This query can be used in PHP code.
I have an ID column in this table, so I need check for duplication for all columns except this ID column:
#need to change values
SET #goodsType = 1, #sybType=5, #deviceId = asdf12345SDFasdf2345;
INSERT INTO `devices` (`goodsTypeId`, `goodsId`, `deviceId`) #need to change tablename and columnsnames
SELECT * FROM (SELECT #goodsType, #sybType, #deviceId) AS tmp
WHERE NOT EXISTS (
SELECT 'goodsTypeId' FROM `devices` #need to change tablename and columns names
WHERE `goodsTypeId` = #goodsType
AND `goodsId` = #sybType
AND `deviceId` = #deviceId
) LIMIT 1;
and now new item will be added only in case of there is not exist row with values configured in SET string
I have table like this
table
id Varchar(45) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name CHAR(30) NOT NULL,
I want to increment my id field like 'LHPL001','LHPL002','LHPL003'... etc.
What should I have to do for that? Please let me know any possible way.
If you really need this you can achieve your goal with help of separate table for sequencing (if you don't mind) and a trigger.
Tables
CREATE TABLE table1_seq
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE table1
(
id VARCHAR(7) NOT NULL PRIMARY KEY DEFAULT '0', name VARCHAR(30)
);
Now the trigger
DELIMITER $$
CREATE TRIGGER tg_table1_insert
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
INSERT INTO table1_seq VALUES (NULL);
SET NEW.id = CONCAT('LHPL', LPAD(LAST_INSERT_ID(), 3, '0'));
END$$
DELIMITER ;
Then you just insert rows to table1
INSERT INTO Table1 (name)
VALUES ('Jhon'), ('Mark');
And you'll have
| ID | NAME |
------------------
| LHPL001 | Jhon |
| LHPL002 | Mark |
Here is SQLFiddle demo
Create a table with a normal numeric auto_increment ID, but either define it with ZEROFILL, or use LPAD to add zeroes when selecting. Then CONCAT the values to get your intended behavior. Example #1:
create table so (
id int(3) unsigned zerofill not null auto_increment primary key,
name varchar(30) not null
);
insert into so set name = 'John';
insert into so set name = 'Mark';
select concat('LHPL', id) as id, name from so;
+---------+------+
| id | name |
+---------+------+
| LHPL001 | John |
| LHPL002 | Mark |
+---------+------+
Example #2:
create table so (
id int unsigned not null auto_increment primary key,
name varchar(30) not null
);
insert into so set name = 'John';
insert into so set name = 'Mark';
select concat('LHPL', LPAD(id, 3, 0)) as id, name from so;
+---------+------+
| id | name |
+---------+------+
| LHPL001 | John |
| LHPL002 | Mark |
+---------+------+
I know it is late but I just want to share on what I have done for this. I'm not allowed to add another table or trigger so I need to generate it in a single query upon insert. For your case, can you try this query.
CREATE TABLE YOURTABLE(
IDNUMBER VARCHAR(7) NOT NULL PRIMARY KEY,
ENAME VARCHAR(30) not null
);
Perform a select and use this select query and save to the parameter #IDNUMBER
(SELECT IFNULL
(CONCAT('LHPL',LPAD(
(SUBSTRING_INDEX
(MAX(`IDNUMBER`), 'LHPL',-1) + 1), 5, '0')), 'LHPL001')
AS 'IDNUMBER' FROM YOURTABLE ORDER BY `IDNUMBER` ASC)
And then Insert query will be :
INSERT INTO YOURTABLE(IDNUMBER, ENAME) VALUES
(#IDNUMBER, 'EMPLOYEE NAME');
The result will be the same as the other answer but the difference is, you will not need to create another table or trigger. I hope that I can help someone that have a same case as mine.
Here is PostgreSQL example without trigger if someone need it on PostgreSQL:
CREATE SEQUENCE messages_seq;
CREATE TABLE IF NOT EXISTS messages (
id CHAR(20) NOT NULL DEFAULT ('message_' || nextval('messages_seq')),
name CHAR(30) NOT NULL,
);
ALTER SEQUENCE messages_seq OWNED BY messages.id;
Is there a way to INSERT a row into a table which contains an AUTO_INCREMENT column, and use the resulting AUTO_INCREMENT value to construct another field in the same row, in a single query?
I know it can be done in two operations.
Our product uses a 16 character numeric account ID stored as CHAR(16). We like to store the account ID as a single field as it is referenced in many tables throughout our database(s), however it is made up of the following parts:
AccountType INT(4) UNSIGNED // never less than 1000
AccountNumber INT(10) UNSIGNED AUTO_INCREMENT PRIMARY
AccountSite INT(2) UNSIGNED
Which are concatenated in order to produce the 16 character accountID field. The AccountType value is never less than 1000.
I would like to be able to perform a single INSERT operation that produces the four fields within the same query, if possible.
I can show this with a stored procedure. You may in the end only want 1 or 2 columns in the table. But I am showing more (assuming a text user friendly string too). That part is trivial, to shrink it down in column count.
Schema:
drop table if exists tX4;
create table tX4
( AccountType INT(4) UNSIGNED not null, -- // never less than 1000
AccountNumber INT(10) AUTO_INCREMENT PRIMARY key,
AccountSite INT(2) UNSIGNED not null,
col4 varchar(50) not null,
weWantThis char(16) not null
);
-- truncate tX4; -- used for testing
Stored Procedure:
DROP PROCEDURE IF EXISTS ins_tX4;
DELIMITER $$
CREATE PROCEDURE ins_tX4
( p_AccountType int,
p_AccountSite int,
p_col4 varchar(50)
)
BEGIN
DECLARE theAI int;
DECLARE sConcatFix char(16);
START TRANSACTION;
insert tX4(AccountType,AccountSite,col4,weWantThis) values (p_AccountType,p_AccountSite,p_col4,'');
set theAI=last_insert_id();
set sConcatFix=concat( lpad(p_AccountType,4,'0'), lpad(theAi,10,'0'), lpad(p_AccountSite,2,'0') );
update tX4 set weWantThis=sConcatFix where AccountNumber=theAI;
COMMIT;
END$$
DELIMITER ;
Test:
call ins_tX4(1,2,'cat');
select * from tX4;
+-------------+---------------+-------------+------+------------------+
| AccountType | AccountNumber | AccountSite | col4 | weWantThis |
+-------------+---------------+-------------+------+------------------+
| 1 | 1 | 2 | cat | 0001000000000102 |
+-------------+---------------+-------------+------+------------------+
Visualized pieces:
0001 0000000001 02
Which is AccountType, the AI, AccountSite (widths 4,10,2) respectively.
call ins_tX4(8765,42,'Sunday');
select * from tX4;
+-------------+---------------+-------------+--------+------------------+
| AccountType | AccountNumber | AccountSite | col4 | weWantThis |
+-------------+---------------+-------------+--------+------------------+
| 1 | 1 | 2 | cat | 0001000000000102 |
| 8765 | 2 | 42 | Sunday | 8765000000000242 |
+-------------+---------------+-------------+--------+------------------+
Column weWantThis could be used as the target for an FK from other tables.
Manual page for LPAD() (Left Padding).
Edit
With an OUT parameter to send back the AI # assigned. Note that parameters are IN parameters by default.
Stored Procedure:
DROP PROCEDURE IF EXISTS ins_tX4;
DELIMITER $$
CREATE PROCEDURE ins_tX4
( p_AccountType int,
p_AccountSite int,
p_col4 varchar(50),
OUT AI_assigned int
)
BEGIN
DECLARE theAI int;
DECLARE sConcatFix char(16);
-- AI means database-assigned AUTO INCREMENT
set AI_assigned = -1; -- assume an Error condition
START TRANSACTION;
insert tX4(AccountType,AccountSite,col4,weWantThis) values (p_AccountType,p_AccountSite,p_col4,'');
set theAI=last_insert_id();
set sConcatFix=concat( lpad(p_AccountType,4,'0'), lpad(theAi,10,'0'), lpad(p_AccountSite,2,'0') );
update tX4 set weWantThis=sConcatFix where AccountNumber=theAI;
set AI_assigned = theAI; -- the OUT parameter is set to the AI value
COMMIT;
END$$
DELIMITER ;
Test from mysql environment:
set #var1 = -1;
call ins_tX4(22,33,'Monday',#var1);
select #var1;
-- 3
call ins_tX4(333,79,'Tuesday',#var1);
select #var1;
-- 4
Test from a PHP environment:
Because this question is tagged PHP, see an answer like the one here from user Matteo Tassinari.
This can be accomplished with a TRANSACTION.
START TRANSACTION;
INSERT INTO accounts (accountType, accountSite) VALUES(1000,10);
UPDATE accounts SET accountID = CONCAT(LPAD(accountType, 4, '0'), LPAD(accountNumber, 10, '0'), LPAD(accountSite, 2, '0')) WHERE accountNumber = LAST_INSERT_ID();
COMMIT;
Which could for example be executed in php as:
$mysqli = new mysqli(...);
$mysqli->begin_transaction();
$query = "INSERT INTO accounts (accountType, accountSite) VALUES(1000,10);"
$query .= "UPDATE accounts SET accountID = CONCAT(LPAD(accountType, 4, '0'), LPAD(accountNumber, 10, '0'), LPAD(accountSite, 2, '0')) WHERE accountNumber = LAST_INSERT_ID();"
$mysqli->query($query);
$mysqli->commit();