SQL Server Identity column does not behave properly? - sql

I created a table named tblEmployees with code -
Create table tblEmployees
(
EmployeeID int identity primary key,
Name nvarchar(30),
Salary float,
Gender tinyint
)
Then I inserted values-
insert into tblEmployees values ('Akmal', 5000, 0)
insert into tblEmployees values ('Shakira', 6000, 1)
insert into tblEmployees values ('Kiron', 7000, 2)
insert into tblEmployees values ('Jamil', 5500, 0)
insert into tblEmployees values ('Faul', 4800, 4)
But, when the values are shown -
EmployeeID Name Salary Gender
2 Akmal 5000 0
3 Shakira 6000 1
4 Kiron 7000 2
5 Jamil 5500 0
7 Faul 4800 4
My question is, why EmployeeID column started with 2? And where is 6? Shouldnt this be incremented automatically?

Do not rely on IDENTITY columns to produce a contiguous set of values with no gaps. Period. This is not guaranteed at all; several things can cause gaps such as rollbacks, deletes, reseeds, etc. I don't believe you reproduced this problem with that exact code above; there was probably other activity in between those INSERT statements.
For such surrogate and meaningless values you really shouldn't care if there are gaps or not. If you care about gaps, use a different technique (e.g. a serializable max()+1 solution) - just be aware that you trade gaps for scalability / concurrency concerns. The other answer (which you accepted, but I suspect will get deleted) said:
If you want to have an identity column with dependable and specified values then you need to set IDENTITY_INSERT to ON on that column, insert your values (with specific ID values) and then set IDENTITY_INSERT to OFF.
This only works if you already know the values you want to insert into that column. Which defeats the purpose of the IDENTITY property in the first place. If you don't already know what values to insert (e.g. what is the "next" ID), it means you need to SELECT MAX() from the table, and add 1 to it. Which means the whole thing needs to be serializable, otherwise someone else can read your same MAX() value and add the same +1 to it. So aside from making the IDENTITY property useless if you're always going to override the generated value anyway, it also kills scalability by effectively limiting concurrency to 1. I highly recommend you strongly weigh that approach before implementing it.
What I suggest you do instead, is use an IDENTITY column, and don't be hung up on gaps. They're going to happen, there's not much you can do about it, and it shouldn't really be a concern anyway. Who cares if there is no employee #6?

Your T-SQL script is incomplete because my IDENTITY values are generated starting with 1 (and ending with 5).
Note #0: I'm only try to describe some reasons for those missing IDENTITY values.
Note #1: Don't run this script on a production server.
Note #2: Using IDENTITY within a column definition means IDENTITY(1,1) <=> IDENTITY(seed value/initial value=1,increment value=1).
Note #3: You should avoid using DBCC CHECKIDENT if you aren't aware about the consequences of this command.
The first missing value (a possible explanation):
why EmployeeID column started with 2?
Run the following script:
IF OBJECT_ID(N'dbo.tblEmployees') IS NOT NULL
DROP TABLE dbo.tblEmployees;
GO
Create table tblEmployees
(
EmployeeID int identity primary key,
Name nvarchar(30),
Salary float,
Gender tinyint
)
GO
insert into tblEmployees values ('Akmal', 5000, 0)
insert into tblEmployees values ('Shakira', 6000, 1)
insert into tblEmployees values ('Kiron', 7000, 2)
insert into tblEmployees values ('Jamil', 5500, 0)
insert into tblEmployees values ('Faul', 4800, 4)
GO
SELECT SCOPE_IDENTITY() AS [Last IDENTITY #1];
/*
Last IDENTITY #1
----------------
5
*/
GO
At this moment the last IDENTITY value generated for this table is (as you can see) 5 and not 7 (like your example).
SELECT * FROM dbo.tblEmployees;
/*
EmployeeID Name Salary G
----------- ------------------------------ ---------------------- -
1 Akmal 5000 0
2 Shakira 6000 1
3 Kiron 7000 2
4 Jamil 5500 0
5 Faul 4800 4
*/
GO
All rows have continuous IDENTITY values: there are no gaps.
Now, for some reasons, somebody deletes all rows from dbo.tblEmployees and also decides to reset (RESEED) the last identity value (which is 5) to 1 (from 5 to 1).
DELETE dbo.tblEmployees;
GO
DBCC CHECKIDENT('dbo.tblEmployees', RESEED, 1);
GO
SELECT SCOPE_IDENTITY() AS [Last IDENTITY #2];
/*
Last IDENTITY #2
----------------
1
*/
GO
Now, the last IDENTITY value is 1 (because of that RESEED 1).
insert into tblEmployees values ('Akmal', 5000, 0)
insert into tblEmployees values ('Shakira', 6000, 1)
insert into tblEmployees values ('Kiron', 7000, 2)
insert into tblEmployees values ('Jamil', 5500, 0)
insert into tblEmployees values ('Faul', 4800, 4)
GO
SELECT * FROM dbo.tblEmployees;
GO
/*
EmployeeID Name Salary Gender
----------- ------------------------------ ---------------------- ------
2 Akmal 5000 0
3 Shakira 6000 1
4 Kiron 7000 2
5 Jamil 5500 0
6 Faul 4800 4
*/
When I insert those rows again, the first generated IDENTITY value is 2 (this time).
Why ? The reason is described in MSDN:
"If no rows have been inserted into the table since the table was created, or if all rows have been removed by using the
TRUNCATE TABLE statement, the first row inserted after you run DBCC CHECKIDENT uses new_reseed_value as the identity.
Otherwise, the next row inserted uses new_reseed_value + the current increment value."
This last formula explains why this time the first IDENTITY value is 2:
new_reseed_value (is 1 - because of RESEED 1) + the current increment value (1 - see Note #2) = 1 + 1 = 2.
Note #4: If you are using TRUNCATE TABLE instead of DELETE then the first row inserted after TRUNCATE TABLE will have the ID = seed value (see Note #2) or new_reseed_value = 1. So, in this case you don't need DBCC(..., RESEED, 1).
The second missing value (a possible explanation):
And where is 6?
DELETE dbo.tblEmployees WHERE EmployeeID = 6
insert into tblEmployees values ('Faul', 4800, 4)
GO
SELECT SCOPE_IDENTITY() AS [Last IDENTITY #3];
/*
Last IDENTITY #3
----------------
7
*/
GO
SELECT * FROM dbo.tblEmployees;
GO
/*
EmployeeID Name Salary Gender
----------- ------------------------------ ---------------------- ------
2 Akmal 5000 0
3 Shakira 6000 1
4 Kiron 7000 2
5 Jamil 5500 0
7 Faul 4800 4
*/

Not sure why my previous answer was voted down and deleted or blocked, but if you want to have an identity column with dependable and specified values then you need to set IDENTITY_INSERT to ON on that column, insert your values (with specific ID values) and then set IDENTITY_INSERT back to OFF.
If you do not care what the values end up being (which is probably the case with an Employees table), then forget about it and learn to live with whatever these values end up being.

Related

Reset identity seed after in SQL Server in table filled with records

I have a Table called Person, this table is non-empty and it's filled with records ( more than 1000 rows )
How can I reset the identity seed of this table?
Id
PersonName
154
Alice
155
John
The query was executed, but the table still has the identity
use [MyDatabase]
DBCC CHECKIDENT ('dbo.Person', RESEED, 0)
GO
Expected result of the table after executing the previous query :
Id
PersonName
1
Alice
2
John
The problem is your understanding; the code is very likely working exactly as it is supposed to, and as I demonstrated.. RESEED resets the value of the next IDENTITY generated, it doesn't change any of the existing values.
Take SQL similar to what I gave in the comments:
CREATE TABLE dbo.Person (ID int IDENTITY(1,1), AbligatoryColumn char(1));
GO
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT N1.N
FROM N N1, N N2, N N3)
INSERT INTO dbo.Person (AbligatoryColumn)
SELECT 'a'
FROM Tally;
GO
SELECT TOP (5) *
FROM dbo.Person
ORDER BY ID ASC;
If you run this you get the follow results:
ID AbligatoryColumn
----------- ----------------
1 a
2 a
3 a
4 a
5 a
Now, let's RESEED the table, and INSERT another row:
DBCC CHECKIDENT ('dbo.Person', RESEED, 0)
GO
INSERT INTO dbo.Person
DEFAULT VALUES;
GO
SELECT TOP (5) *
FROM dbo.Person
ORDER BY ID ASC;
This gives the following data set:
ID AbligatoryColumn
----------- ----------------
1 a
1 NULL
2 a
3 a
4 a
Notice that there are 2 rows where ID has a value of 1. This is because the new row we inserted has used thenew seed, so the next value generated was 1 (as when you RESEED you are defining the last value used, not the next value to be).
Note that you can't UPDATE the value of an IDENTITY, so if we tried the following you would get an error:
UPDATE dbo.Person
SET ID = ID + 1000
WHERE AbligatoryColumn = 'a';
Cannot update identity column 'ID'.
The real question why do you want to change the value? An IDENTITY is just an arbitrary value, it's value doesn't matter.
If you "must" (and I would suggest you don't need to) you would need to CREATE a new table, INSERT the data from your existing table into it (likely with IDENTITY_INSERT enabled) and then DROP your old table, and rename the new one. If you have any foreign key constraints pointing to your current table, you'll need to DROP all of these, and I really hope you have any data referencing the existing ID values, as you'll then need to update all the foreign key values before you recreate the foreign key constraints. As a result your new table would (albeit likely temporarily) need to have both the old and new PK values in separate columns.
So, in truth, leave it as it is; the value of an IDENTITY literally doesn't matter. It's an arbitrary value and it whether the first value starts at 1, 17, or -167 or if there are numbers "missing" is irrelevant to functionality of what IDENTITY is there to achieve; an always ascending value.

Identity specification

I have a problem with identity specification when I create a table in SQL Server 2016.
In column Id I set Identity Increment and Identity Seed equal 1.
Next I add new record to new table.
In column Id show up 2 value. Why? Why not 1 value?
Next drop the first record and add new. In column Id show up 3 value. Why? Why not 1 value.
Next I use command ' update nametable set id=1' and receive answer cannot update identity column Id. Why?
This is probably easier to explain with some code:
CREATE TABLE YourTable (ID int IDENTITY(1,1),
SomeCol varchar(5));
INSERT INTO dbo.YourTable (SomeCol)
VALUES('abc'); --Will get ID 1
INSERT INTO dbo.YourTable (SomeCol)
VALUES('def'),('ghi'); --Will get 2 and 3.
SELECT *
FROM dbo.YourTable;
DELETE FROM dbo.YourTable;
INSERT INTO dbo.YourTable (SomeCol)
VALUES('abc'); --Will get ID 4, because 1-3 have been used. Deleting doesn't let you reuse values.
SELECT *
FROM dbo.YourTable;
DELETE FROM dbo.YourTable;
DBCC CHECKIDENT ('dbo.YourTable', RESEED, 1);
INSERT INTO dbo.YourTable (SomeCol)
VALUES('abc'); --Will get ID 2, as you seeded back to 1; so the NEXT ID is used.
SELECT *
FROM dbo.YourTable;
TRUNCATE TABLE dbo.YourTable;
INSERT INTO dbo.YourTable (SomeCol)
VALUES('abc'); --Will get ID 4, because 1-3 have been used.
SELECT *
FROM dbo.YourTable; --Will get ID 1, as the column was reseed with the TRUNCATE
DROP TABLE dbo.YourTable;
For your specific question on reseeding, the next value after the seed your define is use. The seed you define is the one you are saying was last used. This is covered in the documentation Forcing the current identity value to a new value:
Because the table has existing rows, the next row inserted will use 11
as the value – the new current identity value defined for the column
plus 1 (which is the column's increment value).
The only way to define a table doesn't have existing rows is the TRUNCATE it, which is what I do later on in the above batch (and why 1 is reused).
At the end of the day, the value of your IDENTITY is meaningless other than to provide the row with a single use value (which is not guarenteed to be unique on it's own). Combined with the Primary key/Unique constraints, it makes a good Clustered index candidate, as the next value is always greater than the last used, and values aren't reused.
If having sequential values is important, then what you need to use is a SEQUENCE, not the IDENTITY property. The latter doesn't guarantee uniqueness, or sequential values on it's own (as they could be skipped due to deletes, failed inserts, an unexpected shutdown, etc), but it does guarantee it will not reuse values once they have been (without a RESEED): IDENTITY (Transact-SQL) - Remarks. A SEQUENCE can be used to ensure the values are indeed sequential (apart from due to a DELETE).
Welcome to the forum :)
If you created the table using
Id INT IDENTITY(1,1)
Then the first record inserted will have Id = 1, however, if the insert statement fails or the transaction is rolled back the consumed identity be marked as used (or lost) and the next insert statement will proceed from Id = 2.
Have a look at Microsoft documentation on this topic:
https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property?view=sql-server-2017
When deleting inserted rows (which also happens when those inserts are rolled-back in a transaction, by the way), the identity value is not automatically reset. The identity functionality "remembers" its last value.
Gaps in the identity values will also NOT be filled when older records are deleted from the table and new records are inserted into the table.
That's just how identity works. It's a simple and safe mechanism.
If you (occasionally!) want to reset the identity value, you can take a look at DBCC CHECKIDENT. I personally tend to use it like this:
DBCC CHECKIDENT (MyTable, RESEED, 0) WITH NO_INFOMSGS;
DBCC CHECKIDENT (MyTable, RESEED) WITH NO_INFOMSGS;
(I execute both lines, in this order.)
I would advice against this practice in production environments, however.

Auto increment even and odd for two databases for synchronization without affecting auto increment property

Need quick help. I am having my database with bigint auto increment property. I have database on two location's which need to synchronized. As bigint is not good choice for synchronization because of possibility primary key replica on different sites. I can not move on with GUID because for that i need to change my code as well as database which is not possible for me.
Right now i have two location only for database, so i think if can make my primary key auto increment to be always even at one location and to be odd at other location. It can solve my issue in quick manner.
How can i do it using computed column specification or by any other way. For synchronization i am using Microsoft sycn framework.
If i use identity(1,2) A server or identity(2,2) B server after synchronization it disturbs next increment value. For example if at A server max id 3 and at B server current id is 4. After sync max id on A server will be now 4. I want new id on A server should be 5 only but in reality it inserts 6. How can i solve this issue
Here is a very simple solution, but it will work only for two servers. It can't be easily extended for more servers.
The good thing about it is that it doesn't use CHECKIDENT to reseed the tables and you don't need to worry about simultaneously running transactions to get the accurate MAX ID to feed into CHECKIDENT.
Also, MSDN warns that identity property on a column does not guarantee the following:
Consecutive values after server restart or other failures – SQL Server
might cache identity values for performance reasons and some of the
assigned values can be lost during a database failure or server
restart. This can result in gaps in the identity value upon insert. If
gaps are not acceptable then the application should use its own
mechanism to generate key values. Using a sequence generator with the
NOCACHE option can limit the gaps to transactions that are never
committed.
If you choose a solution that is based on reseeding identity using CHECKIDENT you'd better double check that it works correctly in such cases.
Also, to run CHECKIDENT you may need specific permissions:
Caller must own the table, or be a member of the sysadmin fixed server
role, the db_owner fixed database role, or the db_ddladmin fixed
database role.
Solution
My main idea is that on the first server you use IDENTITY(1,1) and on the second server you use IDENTITY(-1,-1). Rather than trying to make IDs odd and even they will be positive and negative.
Here is a script that proves that it works as intended without any extra work.
-- Sample data
CREATE TABLE #T1 (ID bigint IDENTITY(1,1), V1 int);
CREATE TABLE #T2 (ID bigint IDENTITY(-1,-1), V2 int);
INSERT INTO #T1 VALUES (11);
INSERT INTO #T1 VALUES (12);
INSERT INTO #T1 VALUES (13);
INSERT INTO #T1 VALUES (14);
INSERT INTO #T2 VALUES (21);
INSERT INTO #T2 VALUES (22);
INSERT INTO #T2 VALUES (23);
SELECT * FROM #T1;
SELECT * FROM #T2;
We start with this sample data in our tables:
#T1
ID V1
1 11
2 12
3 13
4 14
#T2
ID V2
-1 21
-2 22
-3 23
Perform the sync
-- Insert into T1 new values from T2
SET IDENTITY_INSERT #T1 ON;
MERGE INTO #T1 AS Dst
USING
(
SELECT ID, V2
FROM #T2
) AS Src
ON Dst.ID = Src.ID
WHEN NOT MATCHED BY TARGET
THEN INSERT (ID, V1)
VALUES (Src.ID, Src.V2);
SET IDENTITY_INSERT #T1 OFF;
-- Insert into T2 new values from T1
SET IDENTITY_INSERT #T2 ON;
MERGE INTO #T2 AS Dst
USING
(
SELECT ID, V1
FROM #T1
) AS Src
ON Dst.ID = Src.ID
WHEN NOT MATCHED BY TARGET
THEN INSERT (ID, V2)
VALUES (Src.ID, Src.V1);
SET IDENTITY_INSERT #T2 OFF;
SELECT * FROM #T1;
SELECT * FROM #T2;
Result of the sync - two identical tables
#T1
ID V1
1 11
2 12
3 13
4 14
-1 21
-2 22
-3 23
#T2
ID V2
-1 21
-2 22
-3 23
1 11
2 12
3 13
4 14
Insert more data to check how identity works after the sync
-- Insert more data into T1 and T2
INSERT INTO #T1 VALUES (15);
INSERT INTO #T1 VALUES (16);
INSERT INTO #T2 VALUES (24);
INSERT INTO #T2 VALUES (25);
INSERT INTO #T2 VALUES (26);
SELECT * FROM #T1;
SELECT * FROM #T2;
-- Clean up
DROP TABLE #T1;
DROP TABLE #T2;
Generated identities after the sync
#T1
ID V1
1 11
2 12
3 13
4 14
-1 21
-2 22
-3 23
5 15
6 16
#T2
ID V2
-1 21
-2 22
-3 23
1 11
2 12
3 13
4 14
-4 24
-5 25
-6 26
You can see that new identities in T1 continue to be positive and new identities in T2 continue to be negative.
Big Edit: (Much better) Option 1:
(Additional note: #VladimirBaranov mentioned this in the comments, and I missed it, but here's a fleshing out of how to use a SEQUENCE in this situation anyway)
My original idea is further down this answer, and would still be potentially viable, but I think this newer option will fit the bill exactly for however many servers you need. It was bothering me that I knew there was a right way to do this in TSQL, and I could not remember what it was. My brain finally dredged it up today: SEQUENCE. SQL Server 2012 and 2014 allow you to define a sequence to generate a series of numbers for use in your tables:
CREATE SEQUENCE oddNums
START WITH 1
INCREMENT BY 2;
GO
CREATE SEQUENCE evenNums
START WITH 0
INCREMENT BY 2;
GO
Then instead of AUTO INCREMENTing your PK's, give them a DEFAULT value from the SEQUENCE (these are the tables from the fiddle linked below):
CREATE TABLE oddMirror (
[id] int PRIMARY KEY DEFAULT NEXT VALUE FOR oddNums,
[data] varchar(7)
);
CREATE TABLE evenMirror (
[id] int PRIMARY KEY DEFAULT NEXT VALUE FOR evenNums,
[data] varchar(7)
);
These sequences are totally unaffected by merges, and will continue to generate odd or even numbers forever no matter what the latest PK in the table is.
Here is a SQLFiddle of this in action.
Note that you can't define the column as IDENTITY if you do this (because of the DEFAULT clause), so you will have to be careful about inserting into your id column, but otherwise this should be about as straightforward as it gets.
This could be done with as many servers as you want, just adjust how much each SEQUENCE increments by and where it starts from, but you would have a hard (not impossible) time adding additional servers to the mix once your SEQUENCEs were defined.
Also, here is an MSDN blog that discusses alternative strategies for simulating a SEQUENCE on prior versions of SQL Server.
(Not as good) Option 2:
(Note: this is my original answer) I have been playing with this some this evening, and depending on how you have things set up, I think you could get away with reseeding the table on each server after the sync is done, based on the current highest id in the table. You would just have to do it slightly differently for each server to keep new ids odd on one and even on the other.
So you have:
CREATE TABLE oddMirror
(id INT NOT NULL IDENTITY(1,2),
data NVARCHAR(10))
GO
and
CREATE TABLE evenMirror
(id INT NOT NULL IDENTITY(2,2),
data NVARCHAR(10)
GO
After you sync the two tables, you don't know if the current identity seed is odd or even, so you need to reset it on each table to the correct "next" value for the server. So, on oddMirror:
DECLARE #maxId INT
DECLARE #newSeed INT
SET #maxId = (SELECT MAX(id) FROM oddMirror)
SET #newSeed = (SELECT CASE WHEN #maxId % 2 = 1 THEN #maxId ELSE #maxId -1 END)
DBCC CHECKIDENT('dbo.oddMirror', RESEED, #newSeed)
GO
And an almost identical process on evenMirror:
DECLARE #maxId INT
DECLARE #newSeed INT
SET #maxId = (SELECT MAX(id) FROM evenMirror)
SET #newSeed = (SELECT CASE WHEN #maxId % 2 = 0 THEN #maxId ELSE #maxId -1 END)
DBCC CHECKIDENT('dbo.evenMirror', RESEED, #newSeed)
GO
So basically, on oddMirror we are saying, "Get the current max id. if it's odd, don't change it, but if it's even, back it up by one."
Then do the same thing on 'evenMirror', except check whether the max id is even instead of odd.
So as an example, take this data:
oddMirror
1,"one"
3,"three"
5,"five"
and
evenMirror
2,"two"
4,"four"
6,"six"
8,"eight"
(notice that evenMirror has more rows)
After a sync, each table would look like this:
oddMirror
1,"one"
2,"two"
3,"three"
4,"four"
5,"five"
6,"six"
8,"eight"
--evenMirror looks the same as this now
Running things through the above queries:
MAX(id) on oddMirror is 8. 8 % 2 = 0, so set #newSeed = 8 - 1 = 7, meaning the next row in oddMirror will get id = 9.
MAX(id) on evenMirror is also 8, but the query is slightly different. 8 % x = 0 so set #newSeed = 8, meaning the next row in 'evenMirrorwill getid = 10`
id = 7 will be skipped in this scenario, but I'm guessing that's not a tremendous concern.
If you then queried:
INSERT INTO oddMirror (data) VALUE ("data")
GO
INSERT INTO evenMirror (data) VALUE ("otherData")
GO
Tables would look like this:
oddMirror
1,"one"
2,"two"
3,"three"
4,"four"
5,"five"
6,"six"
8,"eight"
9,"data"
and
evenMirror
1,"one"
2,"two"
3,"three"
4,"four"
5,"five"
6,"six"
8,"eight"
10,"otherData"
This could theoretically be expanded out to accommodate more number of servers by changing what modulo you take and adding additional WHENs to the CASE statement for each possibility, although that would certainly get cumbersome to maintain. But, we already knew that the right solution (GUIDs) is not available here, and if you're reading this far, the next best solution (SEQUENCEs) may not be available, so whatever we come up with is inevitably going to be cumbersome.
The biggest drawback to this approach is that the tables must be locked until the sync process is complete. If a write comes in before sync is complete and ids are reseeded, there will almost certainly be a collision. If the tables are not written to very often, or you are already locking them for syncing, or if you have a significant "dead" spot in your daily cycle (like 3-4 a.m. or something) where this could be done without serious disruption, it may not be that big of a deal, but only you will know how viable this is.
So, your setup may or may not make this possible, but I have been playing with this quite a bit tonight in my sandbox db, and it seems to work well to make sure that new id's are always odd in one db and always even in the other.
An option, have one server set with identity(1,1), and the other set with identity(-1,-1). Identities will not overlap, and copying data from one server to the other would not affect the "next" id or reseeding.
Doesn't work for more than two servers, of course.
You have two source table for inserting to one destination table :
So I suggest you to do these :
[pkId]: Have an identity field in destination table as PK.
[src]: Add an integer -or any other as you want- field and update it from 1st. source data by1and for the 2nd one by2`.
[Id]: You have also a field that comes from sources.
[nId]: Add a bigint field that is null.
Now use this query to have your enumerated Id in nId field:
Update <table>
Set nId = isnull((select count(ti.*) from <table> as ti where ti.pkId < <table>.pkId), 0) + 1
For running this query after any inserts you can use triggers.
I think with this solution you have all data you need.
Edit
some results:
pkId | src | Id | nId before query | nId after query
-----+-----+----+------------------+--------------------
1 | 1 | 1 | null | 1
2 | 2 | 1 | null | 2
3 | 1 | 2 | null | 3
5 | 2 | 2 | null | 4
6 | 2 | 3 | null | 5
8 | 1 | 3 | null | 6
I think that the even/odd approach is making this very hard. Also, as you add rows to each node, you will have page-split issues, especially if your PK is the clustered index.
Are these tables replicated using peer-to-peer replication or are they manually synchronized? The page split issues would come into play if there is replication involved.
Why not use ranges of numbers for each node? 1-X for node 1, and X+1-Y for Node#2? Estimate your row volume and set the ranges so large that overlap won't occur.
Example for BIGINT:
Node 1: 1-200000000000 (200 billion rows)
Node 2: 200000000001-600000000000 (400 billion rows)
Leaving 600000000001 and up for future use. Caveat, the Identity does not have a maximum value, you need to police that manually.
To set the identity value to the correct number use DBCC CHECKIDENT with the RESEED option. This would also work if you're married to the even/odd scenario.
This also have the advantage of not page splitting once per insert, especially if the activity per node is not evenly balanced.

AutoIncrement Identity Problem

Using SQL Server.
Table1
RecordNo ID
---------------
2 001
3 002
4 003
....
RecordNo column is the Identity column for Table1
Problem
The RecordNo column sometimes starting from 0, some times starting from 2. How to avoid this problem -- RecordNo Column should always start from 1. It should not start from other numbers.
RecordNo ID
--------------
1 001
2 002
3 003
....
Need query Help
The auto-incremented identity number increments whether the record is ultimately committed or not. It looks to me like possibly the initial insert is failing or is inside a transaction which is not being committed.
The identity column type is meant to be a surrogate identifier, and it's not recommended to use that number for anything else. It is possible to build your own autoincrement functionality, but that's generally a bad idea because of performance and concurrency problems.
Also, it is possible to reseed the identity column back to 1 thusly:
dbcc checkident (table1, reseed, 1)
Edit: I assume you have your table definition set properly with Seed set to 1 and increment set to 1 as well.
You can set the start value when you create the table:
Create Table Table1
(
RecordNo int not null Identity(1,1)
, Id char(3) ...
)
IDENTITY [ (seed ,increment ) ]
If you need to reseed a given table then see DBCC CHECKIDENT.

Swap unique indexed column values in database

I have a database table and one of the fields (not the primary key) is having a unique index on it. Now I want to swap values under this column for two rows. How could this be done? Two hacks I know are:
Delete both rows and re-insert them.
Update rows with some other value
and swap and then update to actual value.
But I don't want to go for these as they do not seem to be the appropriate solution to the problem.
Could anyone help me out?
The magic word is DEFERRABLE here:
DROP TABLE ztable CASCADE;
CREATE TABLE ztable
( id integer NOT NULL PRIMARY KEY
, payload varchar
);
INSERT INTO ztable(id,payload) VALUES (1,'one' ), (2,'two' ), (3,'three' );
SELECT * FROM ztable;
-- This works, because there is no constraint
UPDATE ztable t1
SET payload=t2.payload
FROM ztable t2
WHERE t1.id IN (2,3)
AND t2.id IN (2,3)
AND t1.id <> t2.id
;
SELECT * FROM ztable;
ALTER TABLE ztable ADD CONSTRAINT OMG_WTF UNIQUE (payload)
DEFERRABLE INITIALLY DEFERRED
;
-- This should also work, because the constraint
-- is deferred until "commit time"
UPDATE ztable t1
SET payload=t2.payload
FROM ztable t2
WHERE t1.id IN (2,3)
AND t2.id IN (2,3)
AND t1.id <> t2.id
;
SELECT * FROM ztable;
RESULT:
DROP TABLE
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "ztable_pkey" for table "ztable"
CREATE TABLE
INSERT 0 3
id | payload
----+---------
1 | one
2 | two
3 | three
(3 rows)
UPDATE 2
id | payload
----+---------
1 | one
2 | three
3 | two
(3 rows)
NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index "omg_wtf" for table "ztable"
ALTER TABLE
UPDATE 2
id | payload
----+---------
1 | one
2 | two
3 | three
(3 rows)
I think you should go for solution 2. There is no 'swap' function in any SQL variant I know of.
If you need to do this regularly, I suggest solution 1, depending on how other parts of the software are using this data. You can have locking issues if you're not careful.
But in short: there is no other solution than the ones you provided.
Further to Andy Irving's answer
this worked for me (on SQL Server 2005) in a similar situation
where I have a composite key and I need to swap a field which is part of the unique constraint.
key: pID, LNUM
rec1: 10, 0
rec2: 10, 1
rec3: 10, 2
and I need to swap LNUM so that the result is
key: pID, LNUM
rec1: 10, 1
rec2: 10, 2
rec3: 10, 0
the SQL needed:
UPDATE DOCDATA
SET LNUM = CASE LNUM
WHEN 0 THEN 1
WHEN 1 THEN 2
WHEN 2 THEN 0
END
WHERE (pID = 10)
AND (LNUM IN (0, 1, 2))
There is another approach that works with SQL Server: use a temp table join to it in your UPDATE statement.
The problem is caused by having two rows with the same value at the same time, but if you update both rows at once (to their new, unique values), there is no constraint violation.
Pseudo-code:
-- setup initial data values:
insert into data_table(id, name) values(1, 'A')
insert into data_table(id, name) values(2, 'B')
-- create temp table that matches live table
select top 0 * into #tmp_data_table from data_table
-- insert records to be swapped
insert into #tmp_data_table(id, name) values(1, 'B')
insert into #tmp_data_table(id, name) values(2, 'A')
-- update both rows at once! No index violations!
update data_table set name = #tmp_data_table.name
from data_table join #tmp_data_table on (data_table.id = #tmp_data_table.id)
Thanks to Rich H for this technique.
- Mark
Assuming you know the PK of the two rows you want to update... This works in SQL Server, can't speak for other products. SQL is (supposed to be) atomic at the statement level:
CREATE TABLE testing
(
cola int NOT NULL,
colb CHAR(1) NOT NULL
);
CREATE UNIQUE INDEX UIX_testing_a ON testing(colb);
INSERT INTO testing VALUES (1, 'b');
INSERT INTO testing VALUES (2, 'a');
SELECT * FROM testing;
UPDATE testing
SET colb = CASE cola WHEN 1 THEN 'a'
WHEN 2 THEN 'b'
END
WHERE cola IN (1,2);
SELECT * FROM testing;
so you will go from:
cola colb
------------
1 b
2 a
to:
cola colb
------------
1 a
2 b
I also think that #2 is the best bet, though I would be sure to wrap it in a transaction in case something goes wrong mid-update.
An alternative (since you asked) to updating the Unique Index values with different values would be to update all of the other values in the rows to that of the other row. Doing this means that you could leave the Unique Index values alone, and in the end, you end up with the data that you want. Be careful though, in case some other table references this table in a Foreign Key relationship, that all of the relationships in the DB remain intact.
I have the same problem. Here's my proposed approach in PostgreSQL. In my case, my unique index is a sequence value, defining an explicit user-order on my rows. The user will shuffle rows around in a web-app, then submit the changes.
I'm planning to add a "before" trigger. In that trigger, whenever my unique index value is updated, I will look to see if any other row already holds my new value. If so, I will give them my old value, and effectively steal the value off them.
I'm hoping that PostgreSQL will allow me to do this shuffle in the before trigger.
I'll post back and let you know my mileage.
In SQL Server, the MERGE statement can update rows that would normally break a UNIQUE KEY/INDEX. (Just tested this because I was curious.)
However, you'd have to use a temp table/variable to supply MERGE w/ the necessary rows.
For Oracle there is an option, DEFERRED, but you have to add it to your constraint.
SET CONSTRAINT emp_no_fk_par DEFERRED;
To defer ALL constraints that are deferrable during the entire session, you can use the ALTER SESSION SET constraints=DEFERRED statement.
Source
I usually think of a value that absolutely no index in my table could have. Usually - for unique column values - it's really easy. For example, for values of column 'position' (information about the order of several elements) it's 0.
Then you can copy value A to a variable, update it with value B and then set value B from your variable. Two queries, I know no better solution though.
Oracle has deferred integrity checking which solves exactly this, but it is not available in either SQL Server or MySQL.
1) switch the ids for name
id student
1 Abbot
2 Doris
3 Emerson
4 Green
5 Jeames
For the sample input, the output is:
id student
1 Doris
2 Abbot
3 Green
4 Emerson
5 Jeames
"in case n number of rows how will manage......"