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());
Related
I have a table with a unique auto-incremental primary key. Some entries have been deleted from the table, so there are gaps in the ids, it is not a sequence.
I need a query that will make it a sequence. the table is not connected with any other table, so I can temporarily remove the pk and auto-increment from the id column (until the ids will be a sequence).
I use SQL server
If possible, I want to run the query starting from specific id
You cannot update identity column values, nor you can remove the identity from the column, update the values and set it back. You must create a new table with the same schema, copy the data from the old table, but for ID generate a new value, drop the old table and rename the new one to keep the same name.
use [tempdb]
go
if OBJECT_ID('TestTable') is not null
drop table TestTable
go
create table TestTable (
ID int not null primary key clustered identity(1,1)
, Name varchar(50)
)
insert into TestTable(Name) values
('Row #1'),('Row #2'),('Row #3'),
('Row #4'),('Row #5'),('Row #6'),
('Row #7'),('Row #8'),('Row #9')
delete from TestTable where ID in (3, 4, 8) -- Make some gaps
create table TestTable_NEW (
ID int not null primary key clustered identity(1,1)
, Name varchar(50)
)
insert into TestTable_NEW(Name)
select Name
from TestTable
order by ID -- Preserve the rows order
drop table TestTable
exec sp_rename N'TestTable_NEW', N'TestTable'
However, I will not recommend doing this at all. Identity values are supposed to be immutable. They should never change. If you have problems with gaps, then do not allow deletion of existing records. Also, identity do not guarantee that there will be no gaps in the sequence. Only that the values will be unique and increasing. So you should definitely reconsider your database and/or application design, because it is flawed.
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
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.
I have a table that has an AUTO_INCREMENT field. Currently, it is also a PRIMARY KEY.
However, there are situations where I need this AUTO_INCREMENT column to permit duplicates. In other words - two different rows can have the same value inside the AUTO_INCREMENT column. This would mean having an AUTO_INCREMENT field that is not a PRIMARY KEY.
Is this possible?
I'm guessing it's not, since whenever I try to do it, I get this error:
ERROR 1075 (42000) at line 130: Incorrect table definition; there can be only one auto column and it must be defined as a key
I like to have the AUTO_INCREMENT field because it saves me from having to manually store / increment a separate counter elsewhere in my database. I can just insert into the table and grab the value that was inserted. However, if I can't have duplicates, it seems like I'm going to be stuck with using a separate table to track and manually increment this field.
UPDATE: As a quick clarification, I am already familiar with grouping the AUTO_INCREMENT field with another key, as described here. Let's assume for the sake of argument that this solution won't work due to other constraints in the database.
An auto-increment field in MySQL must be part of a key (i.e. an index), but not necessarily part of a primary key or unique key.
CREATE TABLE mytable (
id INT PRIMARY KEY,
otto INT AUTO_INCREMENT,
KEY (otto)
);
-- allow the auto-increment to generate a value
INSERT INTO mytable (id, otto) VALUES (123, DEFAULT);
SELECT * FROM mytable;
> 123, 1
-- specify a duplicate value, overriding the auto-increment mechanism
INSERT INTO mytable (id, otto) VALUES (456, 1);
SELECT * FROM mytable;
> 123, 1
> 456, 1
-- allow the auto-increment to generate another value
INSERT INTO mytable (id, otto) VALUES (789, DEFAULT);
SELECT * FROM mytable;
> 123, 1
> 456, 1
> 789, 2
Sounds like 'subtask' is a table to which 'task' has a FK reference to. That is, if subtasks are reused.
OTOH if a task can have many subtasks, and a subtask can be linked to more than one task then you're looking at many-to-many in a seperate table.
in either case I don't think you want the DB autogenerating these 'linked-IDs'.
I want to make a table in SqlServer that will add, on insert, a auto incremented primary key. This should be an autoincremented id similar to MySql auto_increment functionality. (Below)
create table foo
(
user_id int not null auto_increment,
name varchar(50)
)
Is there a way of doing this with out creating an insert trigger?
Like this
create table foo
(
user_id int not null identity,
name varchar(50)
)
OP requested an auto incremented primary key. The IDENTITY keyword does not, by itself, make a column be the primary key.
CREATE TABLE user
(
TheKey int IDENTITY(1,1) PRIMARY KEY,
Name varchar(50)
)
They have answered your question but I want to add one bit of advice for someone new to using identity columns. There are times when you have to return the value of the identity just inserted so that you can insert into a related table. Many sources will tell you to use ##identity to get this value. Under no circumstances should you ever use ##identity if you want to mantain data integrity. It will give the identity created in a trigger if one of them is added to insert to another table. Since you cannot guarantee the value of ##identity will always be correct, it is best to never use ##identity. Use scope_identity() to get this value instead. I know this is slightly off topic, but it is important to your understanding of how to use identity with SQL Server. And trust me, you did not want to be fixing a problem of the related records having the wrong identity value fed to them. This is something that can quietly go wrong for months before it is dicovered and is almost impossible to fix the data afterward.
As others have mentioned: add the IDENTITY attribute to the column, and make it a primary key.
There are, however, differences between MSSQL's IDENTITY and MySQL's AUTO_INCREMENT:
MySQL requires that a unique
constraint (often in the form of a
primary key) be defined for the
AUTO_INCREMENT column.MSSQL doesn't have such a requirement.
MySQL lets you manually insert values into an AUTO_INCREMENT column.
MSSQL prevents you from manually inserting a value into an IDENTITY
column; if needed, you can override
this by issuing a "SET
IDENTITY_INSERT tablename ON"
command before the insert.
MySQL allows you to update values in an AUTO_INCREMENT column.MSSQL refuses to update values in an
IDENTITY column.
Just set the field as an identity field.
declare the field to be identity
As advised above, use an IDENTITY field.
CREATE TABLE foo
(
user_id int IDENTITY(1,1) NOT NULL,
name varchar(50)
)
As others have said, just set the Identity option.