mysql manual increment ids? - mysql

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.

Related

How to add Identity column into existing table in SQL? [duplicate]

I have an old MS Access DB which I'm translating to a MySQL DB. I used bullzip to create the database but due to bad design the old MS Access database didn't have a unique primary key for most of the tables.
So I've created a id field but obviously it's empty for each entry, I wonder if there's a simple statement I can use to fill them up with 1, 2, 3, 4 etc...
EDIT:
I think I haven't gotten my question across properly. I know all about auto increment. Thats not the problem.
I have a table, full of records which I need kept and which came from a Access database that didn't have a unique id defined as a field. In otherwords I have fields for firstname, surname etc etc but no field 'id'. This seems absolutely crazy but apparently this database has been well used and never had any unique ids for any tables bar one. Weird!
Anyway, I've created a field in the table for id (and set it to auto increment of course) but obviously all the existing records don't have an id set currently. So I need to create one for each record.
Is there a way to fill all these records with unique numbers using a mysql statement?
Cheers
If you add an new id column to an existing table and make it AUTO_INCREMENT PRIMARY KEY, it will automatically populate it with incrementing values.
mysql> ALTER TABLE TheTable ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY;
mysql> SELECT id FROM TheTable;
-- outputs values 1, 2, 3, etc.
If you made an id column but didn't declare it AUTO_INCREMENT PRIMARY KEY, you can populate the column like this:
mysql> SET #id := 0;
mysql> UPDATE TheTable SET id = (#id := #id+1);
Use a predefined AUTO_INCREMENT field, and set the value as NULL when inserting new records, so that it automatically builds up an appropriate incrementer. Aside from that, there is no way (unless using a procedure) to create an incrementing set of values
Use the auto_increment feature of MySQL. MySQL will generate unique numbers for your id column.
For an explanation of the auto_increment feature see http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
If you just want a unique identifier, you can use the uuid() function. It takes up a bit more space than an integer, but it does what you want.
However, I agree with the other answers that you should add an auto increment column and repopulate the table. That is the simplest way to keep the ids unique over time, even as updates takes place, and using a more reasonable amount of storage.
I am not proficient in MySQL, but I have faced this same problem in other DBMS's and here is how I have addressed it when there was an AutoIncrement type facility, but the DBMS had no way to automatically apply it retroactively:
Rename the table you want to add the ID field to. So rename Table1 to Table1_Old.
Create a new Table1 that is a copy of Table1_Old except that it has no data in it.
Add your ID/AutoIncrement column to Table1
Now copy all of the data from Table1_Old to Table1, either skipping or specifying NULL for the ID column. (This is usually a single INSERT..SELECT.. command)
Drop Table1_Old.
The actual specifics and commands vary from DBMS to DBMS, but I have usually been able to find a way to do these steps.
Use AUTO_INCREMENT
CREATE TABLE insect
(
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id),
name VARCHAR(30) NOT NULL,
date DATE NOT NULL,
origin VARCHAR(30) NOT NULL
);
Update
I believe, it seems tough task unless you won't create new table, I will suggest you to use this
SET #rank=0;
SELECT #rank:=#rank+1 AS rank, itemID FROM orders;
It will create a virtual column with the name rank for you, which have unique id value.

MySQL create both entities with cyclic foreign key

I want to have an "Entity" and many versions of it, where one of those versions is the only one which is active/used. It is also possible that the Entity is entirely deactive. So I thought of using two tables with cyclic foreign keys like this:
CREATE TABLE entity (
id int NOT NULL AUTO_INCREMENT,
-- some extra irrelevant data commented out
active_version_id int DEFAULT NULL,
PRIMARY KEY (id)
);
CREATE TABLE entityversion (
id int NOT NULL AUTO_INCREMENT,
-- some extra irrelevant data commented out
entity_id int NOT NULL,
PRIMARY KEY (id)
);
ALTER TABLE entity ADD FOREIGN KEY (active_version_id) REFERENCES entityversion(id) ON DELETE SET NULL;
ALTER TABLE entityversion ADD FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE CASCADE;
I would like to, when creating a new active Entity, to create at the same time its first EntityVersion which will be its active_version. The problem is we don't have their ids yet. Currently, we're creating the Entity with "returning id" and using that to create the EntityVersion, also with "returning id", and then updating the active_version_id of that same Entity, so 3 separate commands like this for example:
INSERT INTO entity DEFAULT VALUES RETURNING id;
-- get the ID back and use it as a parameter to the next command
INSERT INTO entityversion (entity_id) VALUES (%s) RETURNING id;
-- again the same thing
UPDATE entity SET active_version_id = %s WHERE id = %s;
I would like to know if there is a shorter way to do this. I also accept as answer a different approach to the table schemas, if it happens to be the better choice. Thanks for the help!
Create both your rows in a stored procedure, or use a before insert trigger if there is no data that only goes in the entityversion version table. To deal with your cyclical id problem, in mariadb use a sequence instead of auto_increment. In mysql, emulate a sequence with an entity_sequence table that only contains the auto_increment id. In your stored procedure/trigger, get the sequence value (with insert..returning id if emulating a sequence), store entityversion using that value, then set the entityversion id to store in your entity row.
You are implying that the entities are 1:1, in which case they may as well be in the same table. (Make one of the NULLable if it is not to inserted until later.)
If it is 1:many (a 'latest' and many 'older' versions), then the FK only goes one way.
In either case, your "circular" FKs go away.
But to answer your question:
Turn off FK checks
CREATE both tables
Populate both tables
ALTER to add both FKs
Turn on FK checks.
More
Well, it seems that you have many:1, not 1:1. The "History" has a column that is the "id" into the "Current" ('active') table. No circular FKs. Index that column so you can go the other way efficiently. ON DELETE CASCADE is not practical in either direction.
The FK should go one direction, not both.

Change the ids in my SQL Server table so they would be a sequence

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.

Inserting into two MySQL related tables with surogate primary keys

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());

Is it possible to have an AUTO_INCREMENT column that permits duplicates?

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'.