Let's say this is my table:
CREATE TABLE tab (
id INT AUTO_INCREMENT NOT NULL,
val VARCHAR(9),
KEY(id),
PRIMARY KEY (xx)
);
Would it possible to insert multiple rows at the same time in a way that they would all get the same auto-increment value?
The following works, but increments each new row, regardless of the fact that we are doing a single query.
INSERT INTO tab (id,val) VALUES (LAST_INSERT_ID(),'a'), (LAST_INSERT_ID(),'b');
How could I make sure they all receive the same auto-incremented ID in a single query?
You will need to keep the first AI value in a variable and pass it in the INSERT query for different pairs
Related
I need to insert some values into two different tables, this tables need to be linked, let's supposed I have:
table product
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
name TEXT NOT NULL
table ingredients
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
productID INT NOT NULL
value TEXT NOT NULL
so I need to insert in a commit both tables but table ingredients needs to be linked the productID autogenerated by product table, I DON'T want to use LAST_INSERT_ID because I'm inserting multiples rows at the same time and I may get and id from other row.
You can use LAST_INSERT_ID() since is multi-thread safe. MySQL's docs say:
For LAST_INSERT_ID(), the most recently generated ID is maintained in the server on a per-connection basis. It is not changed by another client.
I have a column in my db "id" int(11) NOT NULL AUTO_INCREMENT, and I want multiple rows in this table with the same id value. So when inserting to the table I'd like to tell whether it should increment or the value remains the sasme. Is there any easy way how to do that?
As MySQL documentation on auto_increment says (highlighting is mine):
No value was specified for the AUTO_INCREMENT column, so MySQL
assigned sequence numbers automatically. You can also explicitly
assign 0 to the column to generate sequence numbers, unless the
NO_AUTO_VALUE_ON_ZERO SQL mode is enabled. If the column is declared
NOT NULL, it is also possible to assign NULL to the column to generate
sequence numbers. When you insert any other value into an
AUTO_INCREMENT column, the column is set to that value and the
sequence is reset so that the next automatically generated value
follows sequentially from the largest column value.
This means, if you determine before the insert the current maximum of the auto_increment field and you explicitly insert that value in the insert statement, then you can have duplicate values in the auto_increment field.
There a couple things that you need to pay attention to:
If you can have parallel inserts into the table, then you may have to lock the table for reading, so another process does not insert a new record triggering the increment of the field.
You cannot use primary / unique index constraint on the auto_increment field.
The alternative is to have a separate table just for the auto_increment and do not use auto_increment in the main table. If you need a new id, then just insert a record into the auto_ncrement table a get the incremented id and use that to insert a record into the main table. Otherwise, just fetch the id value from the main table and use it in the insert.
An auto_increment column makes sure that the values in it are unique ! So, you can't do it this way.
I'd suggest a trigger instead , combining the needed logic.
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.
CREATE TABLE animals (
grp ENUM('fish','mammal','bird') NOT NULL,
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (grp,id)
) ENGINE=MyISAM;
INSERT INTO animals (grp,name) VALUES
('mammal','dog'),('mammal','cat'),
('bird','penguin'),('fish','lax'),('mammal','whale'),
('bird','ostrich');
SELECT * FROM animals ORDER BY grp,id;
Which returns:
**grep id name**
fish 1 lax
mammal 1 dog
mammal 2 cat
mammal 3 whale
bird 1 penguin
bird 2 ostrich
If the AUTO_INCREMENT column is part of multiple indexes, MySQL generates sequence values using the index that begins with the AUTO_INCREMENT column, if there is one. For example, if the animals table contained indexes PRIMARY KEY (grp, id) and INDEX (id), MySQL would ignore the PRIMARY KEY for generating sequence values. As a result, the table would contain a single sequence, not a sequence per grp value
In my database, all Primary Keys are surogate. There are some Unique keys, but not always, so the most safe way to access specific row is Primary Key. Many of them use AUTO_INCREMENT. Do I have to lock access to database when inserting into two related table? For example.
create table foo
(
foo_id numeric not null auto_increment,
sth varchar,
PRIMARY KEY(foo_id)
)
create table bar
(
bar_id numeric not null auto_increment,
foo_id numeric not null,
PRIMARY KEY(bar_id),
FOREIGN KEY (foo_id) REFERENCES foo(foo_id)
)
First I insert sth to foo, and then I need foo_id value to insert related stuff into bar. This value I can get from INFORMATION_SCHEMA.TABLES. But what if somebody will add new row into foo before I get the auto_increment value? If all these steps are in stored procedure is there implicitly started transactions which locks all needed resources for one procedure call? Or maybe I have to use explicitly START TRANSACTION. What if I dont use procedure - just sequence of inserts and selects?
Instead of looking in INFORMATION_SCHEMA.TABLE, I would suggest that you use LAST_INSERT_ID.
From the MySQL documentation: The ID that was generated is maintained in the server on a per-connection basis. This means that the value returned by the function to a given client is the first AUTO_INCREMENT value generated for most recent statement affecting an AUTO_INCREMENT column by that client.
This imply that an insert done at the same time on a different connection will not change the value that is returned on your current connection.
Run queries in that sequence:
INSERT INTO foo (sth) VALUES ('TEST');
Than:
INSERT INTO bar (foo_id) VALUES (SELECT LAST_INSERT_ID());
I have created a trigger to help me to make the primary key when I insert a row. I want to get the primary key I just insert. But because the primary key is not auto_increment I cannot use
SELECT LAST_INSERT_ID();
So how can I get the primary key?
Here is the trigger
CREATE TRIGGER before_insert_user_info
BEFORE INSERT ON USER_INFO
FOR EACH ROW
SET new.uID = CONCAT('U', ((SELECT MAX(CAST(SUBSTRING(uID, 2, length(uID)) AS UNSIGNED)) FROM USER_INFO)+1));
And here is the insert
INSERT INTO USER_INFO(name) VALUES ('Peter');
It is kind of unusual to rely on a trigger to generate a primary key. There is no way to retreive data back from a trigger (e.g. it has no return value). But you can reuse your tigger's logic to retreive the generated value:
INSERT INTO user_info VALUE (#newUID, ...);
SELECT MAX(CAST(SUBSTRING(#newUID, 2, LENGTH(#newUID)) AS UNSIGNED)) AS last_insert_id
FROM user_info; -- this is the generated value
Wrap these two statements in a transaction to make sure a new user is not inserted in between.
As an alternative, I would create another INT AUTO_INCREMENT column, so that you can retreive your new row after insertion with LAST_INSERT_ID();
And while we are at it, I would make this new field the (surrogate) primary key. The same trigger could still generate the "public" user ID, but then we would be back to a more usual architecture.
Last food for thoughts: do you really need to store your user ID's with the U prefix? Perhaps you could just store a plain INT value, and preppend the U on selection.
I have a table with items in it (id, name, etc) and I want some kind of database scheme to be able to have multiple copies of the item while still being able to increment the ids. There will be a field called startdate or date_from_which_this_entry_should_be_used.
I've thought of two ways of implementing this:
Have a table with only ids (primary key, auto-increment) and do joins to a table that has all the item information.
Advantages:
easy to understand
hard for someone that comes after me to get confused
Disadvantages:
requires more debugging and coding since this system is already in use
seems weird to have a table with a single field
Have a single table using a sub-select to get the MAX value (+1) to apply to new items.
Advantages:
single table
only minor code adjustments (but not all that different, maybe)
Disadvantages:
prone to errors (manual increment, can't allow deletions or the MAX value might be off)
Thanks in advance!
You should create a table called item_ids or something to generate id values. It's okay that this has only a single column.
CREATE TABLE item_ids (
item_id INT AUTO_INCREMENT PRIMARY KEY
) ENGINE=InnoDB;
You don't even need to commit any data to it. You just use it to generate id values:
START TRANSACTION;
INSERT INTO item_ids DEFAULT VALUES;
SET #id = LAST_INSERT_ID();
ROLLBACK;
So now you have a concurrency-safe method to create new id's.
Then you make a compound primary key for your items table. You must use MyISAM for this.
CREATE TABLE items (
item_id INT,
seq_id INT AUTO_INCREMENT,
name VARCHAR(20),
etc VARCHAR(20),
PRIMARY KEY (item_id, seq_id)
) ENGINE=MyISAM;
MyISAM supports an auto-increment column in a compound primary key, which will start over at value 1 for each new item_id.* It also uses MAX(item_id)+1 so if you delete the last one, its value will be reallocated. This is unlike other use of AUTO_INCREMENT where a deleted value is not re-used.
Whether you insert a new item, or whether you insert a new copy of an existing item, you use a similar INSERT:
INSERT INTO items (item_id, name, etc) VALUES (#id, 'Stephane', 'etc');
The #id parameter is either a value of an existing item, or else the auto-generated value you got from the item_ids table.
* InnoDB supports auto-increment only as the first column of a primary or unique key, and it does not start over the count for each distinct value of the other column.