renumber primary key - sql

How would I reset the primary key counter on a sql table and update each row with a new primary key?

I would add another column to the table first, populate that with the new PK.
Then I'd use update statements to update the new fk fields in all related tables.
Then you can drop the old PK and old fk fields.
EDIT: Yes, as Ian says you will have to drop and then recreate all foreign key constraints.

Not sure which DBMS you're using but if it happens to be SQL Server:
SET IDENTITY_INSERT [MyTable] ON
allows you to update/insert the primary key column. Then when you are done updating the keys (you could use a CURSOR for this if the logic is complicated)
SET IDENTITY_INSERT [MyTable] OFF
Hope that helps!

This may or not be MS SQL specific, but:
TRUNCATE TABLE resets the identity counter, so one way to do this quick and dirty would be to
1) Do a Backup
2) Copy table contents to temp table:
3) Copy temp table contents back to table (which has the identity column):
SELECT Field1, Field2 INTO #MyTable FROM MyTable
TRUNCATE TABLE MyTable
INSERT INTO MyTable
(Field1, Field2)
SELECT Field1, Field2 FROM #MyTable
SELECT * FROM MyTable
-----------------------------------
ID Field1 Field2
1 Value1 Value2

Why would you even bother? The whole point of counter-based "identity" primary keys is that the numbers are arbitrary and meaningless.

you could do it in the following steps:
create copy of yourTable with extra column new_key
populate copyOfYourTable with the affected rows from yourTable along with desired values of new_key
temporarily disable constraints
update all related tables to point to the value of new_key instead of the old_key
delete affected rows from yourTable
SET IDENTITY_INSERT [yourTable] ON
insert affected rows again with the new proper value of the key (from copy table)
SET IDENTITY_INSERT [yourTable] OFF
reseed identity
re-enable constraints
delete the copyOfYourtable
But as others said all that work is not needed.
I tend to look at the identity type primary keys as if they were equivalent of pointers in C, I use them to reference other objects but never modify of access them explicitly

If this is Microsoft's SQL Server, one thing you could do is use the [dbcc checkident](http://msdn.microsoft.com/en-us/library/ms176057(SQL.90).aspx)
Assume you have a single table that you want to move around data within along with renumbering the primary keys. For the example, the name of the table is ErrorCode. It has two fields, ErrorCodeID (which is the primary key) and a Description.
Example Code Using dbcc checkident
-- Reset the primary key counter
dbcc checkident(ErrorCode, reseed, 7000)
-- Move all rows greater than 8000 to the 7000 range
insert into ErrorCode
select Description from ErrorCode where ErrorCodeID >= 8000
-- Delete the old rows
delete ErrorCode where ErrorCodeID >= 8000
-- Reset the primary key counter
dbcc checkident(ErrorCode, reseed, 8000)
With this example, you'll effectively be moving all rows to a different primary key and then resetting so the next insert takes on an 8000 ID.
Hope this helps a bit!

Related

How to ensure that there are no duplicates in field? MS SQL Server 2014

I have the following table:
Customer (Id, Name, employeeID)
The table is already created and is empty, I don't want to remove duplicate data, all I want is to ALTER the table to ensure that there will be no duplicate data
I want to use ALTER and ensure that there are no duplicates in employeeID.
Will
ALTER TABLE Customers
UNIQUE(employeeID)
ADD CONSTRAINT
Is there a better way?
Adding a unique constraint will ensure that no duplicate entries will be added in future:
ALTER TABLE Customers
ADD CONSTRAINT choose_a_name_for_the_constraint UNIQUE (EmployeeID);
You had it basically right, just a bit of a keyword order problem..
If you're working with SQLS, consider also that trivial operations like this can be done via the GUI in SSMS, and it will guide the process. You can also get it to turn the changes into scripts for you by right clicking the table and choosing "Script Table As..." so you can use them elsewhere
From my understanding, I create Unique Index as follows,
create table unicondtional (
i int identity (1,1)
, j int
)
insert into unicondtional values (1), (1)
select * from unicondtional
-- assume 'unicondtional' is table like what you have, so far.
CREATE UNIQUE NONCLUSTERED INDEX unique_with_condition ON unicondtional
(
j
)
WHERE (i > 2) -- max (i)
-- create unique index with condition.
-- from the 'where' clause, we say that, Index should be ensure the unique value insertion.
insert into unicondtional values (1), (2), (3) -- See the Note.
-- successful insert.
select * from unicondtional
insert into unicondtional values (2)
-- due to the Unique Index, duplicate is not allowed by the Index.
update unicondtional
set j = 3
where j = 1
-- before the Index(On the first two rows), duplicates are exist.
select * from unicondtional
So, you don't need to delete the existing duplicate records.
Note: After the Index, if you consider the 1 as duplicate, then you go with Trigger instead of Unique Index.
Since your table is empty, you can directly run
ALTER TABLE Customers
ADD CONSTRAINT UQ_EmployeeID UNIQUE(EmployeeId);
That will ensure there is no duplicate EmployeeId can be added in that table.
But if there is some data in the table and there is already a duplicate EmployeeId you will get an error message
The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'Customers' and the index name 'UQ_EmployeeId'. The duplicate key value is ("DuplicateValueHere").
For your question
Is there a better way?
You already have the better way to prevent inserting duplicates.
See
Create Unique Constraints
and
ALTER TABLE (Transact-SQL)

Copy a SQL record from a backup and insert into database, keeping the primary key

I need to restore a single row in a table which has been deleted. I have the row in a backup copy of the database. I know that I can copy / paste from one database to another, however (obviously) doing this doesn't keep the primary key value, so it looks like a new record.
How can I insert this row into a table, and keep the original primary key value (which is unique)? There are other tables which reference this keyid, so doing this will prevent us having to make changes elsewhere.
Assuming that this is an IDENTITY Column (which performs an auto-insert of an INT/BIGINT which increments) then you would need to run the SET IDENTITY_INSERT command.
SET IDENTITY_INSERT dbo.MyTable ON;
INSERT INTO dbo.MyTable (Id, Columns) SELECT Id, Columns FROM BackupDb.dbo.MyTable WHERE Id = DeletedRowID
SET IDENTITY_INSERT dbo.MyTable OFF;
Here's the MS Doc related to this command https://learn.microsoft.com/en-us/sql/t-sql/statements/set-identity-insert-transact-sql
Any other type of primary key (such as a Unique Identifier populated by NEWID() should not require IDENTITY_INSERT to be turned on.
Also note that only 1 table can have IDENTITY_INSERT turned on at a time, and the account issuing the command must own or have the ALTER permission on the table.
Let's say your table is set up like this
create table tableWithAutoId (id int identity(1,1) primary key, val varchar(50))
Just doing this (which is basically what you want) will fail
insert into tableWithAutoId(id, val)values(1,'abc')
Cannot insert explicit value for identity column in table 'tableWithAutoId' when IDENTITY_INSERT is set to OFF.
However if you turn on Identity_insert first you can ignore the Identity property on the column
set identity_insert tableWithAutoId ON
insert into tableWithAutoId(id, val)values(1,'abc')
Be sure to turn it back OFF when you're done
set identity_insert tableWithAutoId ON
insert into (column list)
select (column list) from
where = value

How to change the primary key value in sql server?

I am using SQL server 2014.
In my table, I set primary key for the column.The primary key value starts from 1. I want to change it to start from 0. How can I achieve this?
If the table is empty, the easiest thing to do would be to drop the table and recreate it with an identity seed of 0 like GuidoG did. If the table has data, changing the primary key to a 0 (like Intern87 mentions) would be a bad idea because after it inserts a row with a key of 0 the next key would be 1 which would probably already be in the table and therefore would cause further inserts to fail with a primary key duplication error.
So if you have existing data but you MUST have data with a key of 0, I would probably just do an identity insert with row 0. Do this with the following
SET IDENTITY_INSERT mytable ON;
INSERT INTO mytable (id, col1, col2, etc..)
VALUES (0, 'col1data','col2data', etc..);
SET IDENTITY_INSERT mytable OFF;
just make sure to run all of that at once because once you turn identity insert on then other inserts may fail till you turn it off.
this creates a table where the autoinc fields starts with 0 in stead of 1
Also it is best to name your primary key
CREATE TABLE myTable
(
id int IDENTITY(0,1),
othercolumn int,
and so on...
constraint PK_myTableID primary key (id)
)
If however you want to do this with an existing table than your best option is to use Element Zero's anwser
You may be able to reseed it;
DBCC CHECKIDENT ('TableName', RESEED, 0);
GO

Is there a smart way to append a number to an PK identity column in a Relational database w/o total catastrophe?

It's far from the ideal situation, but I need to fix a database by appending the number "1" to the PK Identiy column which has FK relations to four other tables. I'm basically making a four digit number a five digit number. I need to maintain the relations. I could store the number in a var, do a Set query and append the 1, and do that for each table...
Is there a better way of doing this?
You say you are using an identity data type for your primary key so before you update the numbers you will have to SET IDENTITY_INSERT ON (documentation here) and then turn it off again after the update.
As long as you have cascading updates set for your relations the other tables should be updated automatically.
EDIT: As it's not possible to change an identity value I guess you have to export the data, set the new identity values (+10000) and then import your data again.
Anyone have a better suggestion...
Consider adding another field to the PK instead of extending the length of the PK field. Your new field will have to cascade to the related tables, like a field length increase would, but you get to retain your original PK values.
My suggestion is:
Stop writing to the tables.
Copy the tables to new tables with the new PK.
Rename the old tables to backup names.
Rename the new tables to the original table name.
Count the rows in all the tables and double check your work.
Continue using the tables.
Changing a PK after the fact is not fun.
If the column in question has an identity property on it, it gets complicated. This is more-or-less how I'd do it:
Back up your database.
Put it in single user mode. You don't need anybody mucking around whilst you do the surgery.
Execute the ALTER TABLE statements necessary to
disable the primary key constraint on the table in question
disable all triggers on the table in question
disable all foreign key constraints referencing the table in question.
Clone your table, giving it a new name and a column-for-column identical definitions. Don't bother with any triggers, indices, foreign keys or other constraints. Omit the identity property from the table's definition.
Create a new 'map' table that will map your old id values to the new value:
create table dbo.pk_map
(
old_id int not null primary key clustered ,
new_id int not null unique nonclustered ,
)
Populate the map table:
insert dbo.pk_map
select old_id = old.id ,
new_id = f( old.id ) // f(x) is the desired transform
from dbo.tableInQuestion old
Populate your new table, giving the primary key column the new value:
insert dbo.tableInQuestion_NEW
select id = map.id ,
...
from dbo.tableInQuestion old
join dbo.pk_map map on map.old_id = old.id
Truncate the original table: TRUNCATE dbo.tableInQuestion. This should work—safely—since you've disabled all the triggers and foreign key constraints.
Execute SET IDENTITY_INSERT dbo.tableInQuestion ON.
Reload the original table:
insert dbo.tableInQuestion
select *
from dbo.tableInQuestion_NEW
Execute SET IDENTITY_INSERT dbo.tableInQuestion OFF
Execute drop table dbo.tableInQuestion_NEW. We're all done with it.
Execute DBCC CHECKIDENT( dbo.tableInQuestion , reseed ) to get the identity counter back in sync with the data in the table.
Now, use the map table to propagate the changed primary key column down the line. Depending on your E-R model, this can get complicated as foreign keys referencing the updated column may themselves be part of a composite primary key.
When you're all done, start re-enabling the constraints and triggers you disabled. Make sure you do this using the WITH CHECK option. Fix any problems thus uncovered.
Finally, drop the map table, and clear the single user flag and bring your system(s) back online.
Piece of cake! (or something.)
Consider this approach:
Reset the identity seed to the 10000 + the current seed.
Set identity insert on
Insert into the table from the values in the table and add 10000 to the identity column on the way.
EX:
Set identity insert on
Insert Table(identity, column1, eolumn2)
select identity + 10000, column1, column2
From Table
Where identity < origional max identity value
After the insert you know the identity is exactly 10000 more than the origional.
Update the foreign keys by addding 10000.

How reorder primary key?

I have deleted one row(row 20) in my "table category" ,please let me know that how can i reorder the catid (primary key)? at this time it is 21 after 19.
Thanks
You cannot. The closest you can get is truncate table, which will drop the table and recreate it, which means you lose all data in it, and the ID counter is reset to 0. Other than that, ID will always increment by one from the last inserted record, no matter if that record still exists or not. You can write a query to fix all your current IDs, of course, but upon next insert, it'll still create a new gap. More to the point: if a sequential ordering without gaps is what you want, auto incremental ID is not the proper way to achieve that. Add another int field where you manually keep track of this ordering.
If you care enough about your primary key values that such a value is unwanted, you shouldn't be using auto-number primary keys in the first place.
The whole point with a auto-number key is that you say "As long as the key is unique, I don't really care about its value."
Don't mess with the primary keys. They should never change and you should not use them in your app for anything but joining tables.
Add a new column if you need a gapless index and update this column accordingly when you do inserts/removes. This might sound like useless work for you right now, but it will save you a lot of pain later.
Try this:
UPDATE tbl SET catid = (SELECT COUNT(*) FROM tbl t WHERE t.catid <= tbl.catid);
You might also want to rethink / redesign. Renumbering the entire table when you delete a row doesn't seem likely to be either practical or necessary.
Actually you can.
If your rows have unique enough data and you are using PHPmyAdmin
Delete the Column with the Primary ID
Read the Column with Primary Key and Auto Increment enabled.
What do you mean by reordering primary key? If you are saying that you want the primary key to take 20 instead of 21, then I afraid you can't do that straightaway.
All you can do, is to drop the primary key constraint, then change the 21 to 20, and reapply back the primary key constraint
David is right about not using primary key for indexing and such.
If you'll just have to change a particular primary key value once (I've done it sometimes during migration) you could of course set identity_insert on and copy the row with a insert select and then delete the original one.
For recreating a sort order or an column used as an index in your application you could use the following stored procedure:
CREATE PROCEDURE [dbo].[OrganizeOrderConfirmationMessages]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sortOrder INT;
SET #sortOrder = 0;
-- // Create temporary table
CREATE TABLE #IDs(ID INT, SortOrder INT)
-- // Insert IDs in order according to current SortOrder
INSERT INTO #IDs SELECT ocm.ID, 0 FROM OrderConfirmationMessages ocm ORDER BY ocm.SortOrder ASC
-- // Update SortOrders
UPDATE #IDs SET SortOrder = #sortOrder, #sortOrder = #sortOrder + 10
-- // Update the "real" values with data from #IDs
UPDATE OrderConfirmationMessages SET SortOrder = x2.SortOrder
FROM #IDs x2 WHERE OrderConfirmationMessages.ID = x2.ID
END
Results:
An example of SortOrders will go from 1,2,5,7,10,24,36 to 10,20,30,40,50,60,70
You should drop the 'catid' field and then create it again, set it as primary and check the Auto Increment checkbox, it will add the new field and fill the numbers.
First drop the primary key column from your table and run this syntax in your phpmyadmin sql section-
ALTER TABLE 'your_tablename' ADD 'column_name' BIGINT NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY
('column_name' (10));
This will automatically arrange the column in numbers from 0, 1 and so on.
try this:
SET #var:=0;
UPDATE `table` SET `id`=(#var:=#var+1);
ALTER TABLE `table` AUTO_INCREMENT=1;
In postgres, you can do this where number of records < 300:
update schema.tbl1
set tbl_id = tbl_id + 300;
alter sequence schema.tbl1_id_seq
restart with 1;
insert into schema.tbl1
select nextval('schema.tbl1_id_seq'),
column2,
column3
from schema.tbl1;
delete from schema.tbl1
where tbl1_id > 300;