Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / database / SQL-Server / SQL-Server-2014

Having Not ONE But TWO Identities in Single SQL Server Table

1.92/5 (5 votes)
12 Apr 2016CPOL5 min read 9.8K   19  
Having not ONE but TWO Identities in a single SQL Server Table

Introduction

This article will introduce the users, especially the SQL Server developers to a limitation of SQL Server that it does not allow a user to apply IDENTITY to more than one Columns in a Table. After the introduction, we will look at several ways that we can address the limitation. In the last solution, i.e., Solution #4, we will look at how to address the limitation in a way in which it almost seems as if we have created not one but two columns with IDENTITY in a single table.

Background

SQL Server does not allow to create identity on two columns in a single table. What if we have a scenario in which we have to require identity on two columns in single table? Well, there are multiple ways to do that. Starting with the Issue or the Error, let's look at them one by one...

The Issue/Error: Creating Table with IDENTITY on Two Columns

If a user tries to create a Table in SQL Server with IDENTITY on two Columns, SQL Server does not allow it and returns an error. Suppose we want to create a Table with the name tblTempTable which will have three Columns. The Columns are:

  1. ID with datatype INT. This is the Primary Key and has IDENTITY with SEED value 1 and INCREMENT of 1.
  2. ExtraID with datatype INT. The is the second Column on which we want to have IDENTITY with SEED value 2 and INCREMENT of 2.
  3. TempName with datatype VARCHAR(100). This Column will store the concerned data.

The code to create table is:

SQL
CREATE TABLE tblTempTable (
	ID INT PRIMARY KEY IDENTITY(1, 1),
	ExtraID INT IDENTITY(2, 2), -- NEED TO IMPLEMENT IDENTITY ON THIS COLUMN AS WELL
	TempName VARCHAR(100)
);
GO

Now, when we execute the code to create table, SQL Server returns the error:

Msg 2744, Level 16, State 2, Line 1
Multiple identity columns specified for table 'tblTempTable'. 
Only one identity column per table is allowed.
Code can also be found in .SQL file by the name "01 - The Error.sql".

Now how are we going to address this issue. Obviously, we cannot create IDENTITY on two columns in a single Table. So there must be a workaround.

Solution#1. Select the Identity Value before Operation

There are multiple ways in which a user can select the next Identity Value. Simply either a user can SELECT the value before the INSERT statement or user may define a User-Defined function for this purpose.

First, we create the Table but the legitimate code will require us to have only IDENTITY on just one Column. So, the code for creating the Table is as follows:

SQL
CREATE TABLE tblTempTable (
	ID INT PRIMARY KEY IDENTITY(1, 1),
	ExtraID INT NOT NULL, -- NEED TO IMPLEMENT IDENTITY ON THIS COLUMN
	TempName VARCHAR(100)
);
GO

We also create three variables to create a new Identity for Column ExtraID. The code is as follows:

SQL
DECLARE @ExtraID AS INT; -- THIS VARIABLE WILL HAVE THE NEW ID
DECLARE @SeedValue AS INT = 0; -- THE SEED VALUE FOR THE IDENTITY
DECLARE @IncrementValue AS INT = 2; -- THE SEED VALUE FOR THE IDENTITY

Now before executing the INSERT statement, we SELECT the new value for ExtraID Column in a variable and pass the variable in INSERT statement. The code is as follows:

SQL
SELECT @ExtraID = ISNULL(MAX(ExtraID),   _
(@SeedValue - @IncrementValue)  ) + @IncrementValue FROM tblTempTable;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Aqeel');

SELECT @ExtraID = ISNULL(MAX(ExtraID),   _
(@SeedValue - @IncrementValue)  ) + @IncrementValue FROM tblTempTable;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Rizwan');

SELECT @ExtraID = ISNULL(MAX(ExtraID),   _
(@SeedValue - @IncrementValue)  ) + @IncrementValue FROM tblTempTable;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Shariq');

SELECT @ExtraID = ISNULL(MAX(ExtraID),   _
(@SeedValue - @IncrementValue)  ) + @IncrementValue FROM tblTempTable;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Raheel');

SELECT @ExtraID = ISNULL(MAX(ExtraID),   _
(@SeedValue - @IncrementValue)  ) + @IncrementValue FROM tblTempTable;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Abu Bakar');

SELECT @ExtraID = ISNULL(MAX(ExtraID),   _
(@SeedValue - @IncrementValue)  ) + @IncrementValue FROM tblTempTable;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Waseem');
Code can also be found in .SQL file by the name "02 - Select before Operation.sql".

How good is a software engineer if he/she cannot write a reusable piece of code. So in the next step, we create a User-Defined Function to address the issue in a better way.

Solution#2. Select the Identity Value using User-Defined Function

User-Defined Function provides a way for software engineers to create a reusable piece of code. In this case, the user will create a Scalar Function that will return the new value for ExtraID Column. The code to create the function is as follows:

SQL
CREATE FUNCTION dbo.NewTempTableExtraID
(
	@SeedValue INT,
	@IncrementValue INT
)
RETURNS INT
AS
BEGIN
	DECLARE @NewExtraID AS INT;
	SELECT @NewExtraID = ISNULL(MAX(ExtraID), _
		(@SeedValue - @IncrementValue)) + @IncrementValue FROM tblTempTable;
	RETURN @NewExtraID;
END;
GO

Note that this function accepts two parameters, i.e., @SeedValue and @IncrementValve. Both parameters are self-explanatory. This function will select and return the next value for ExtraID Column. The User-Defined function implements the same logic from the previous section.

Now instead of selecting the new value for ExtraID column before the INSERT statement, the user will call the Function in the INSERT statement and this will insert new value in Table.

SQL
INSERT INTO tblTempTable (ExtraID, TempName) _
	VALUES (dbo.NewTempTableExtraID(@SeedValue, @IncrementValue), 'Aqeel');
INSERT INTO tblTempTable (ExtraID, TempName) _
	VALUES (dbo.NewTempTableExtraID(@SeedValue, @IncrementValue), 'Rizwan');
INSERT INTO tblTempTable (ExtraID, TempName) _
	VALUES (dbo.NewTempTableExtraID(@SeedValue, @IncrementValue), 'Shariq');
INSERT INTO tblTempTable (ExtraID, TempName) _
	VALUES (dbo.NewTempTableExtraID(@SeedValue, @IncrementValue), 'Raheel');
INSERT INTO tblTempTable (ExtraID, TempName) _
	VALUES (dbo.NewTempTableExtraID(@SeedValue, @IncrementValue), 'Abu Bakar');
INSERT INTO tblTempTable (ExtraID, TempName) _
	VALUES (dbo.NewTempTableExtraID(@SeedValue, @IncrementValue), 'Waseem');

This is how we optimize and reuse the code and avoid the factor of human error.

Code can also be found in .SQL file by the name "03 - User Defined Function.sql".

Solution#3. Create a Sequence Object

SQL Server 2012 introduced a new user-defined schema-bound object by the name SEQUENCE. User can create this object and can configure multiple attributes including START value, INCREMENT value, MINVALUE, and MAXVALUE (for more details, see point 1 in references).

Now what we do is we create a SEQUENCE object. The code to create a SEQUENCE object is as follows:

SQL
CREATE SEQUENCE dbo.seqExtraID AS INT START WITH 2 INCREMENT BY 2;

Now that we have a SEQUENCE object by the name seqExtraID, we retrieve the new value for ExtraID Column and pass it to the INSERT statement and insert it into the Table.

SQL
SELECT @ExtraID = NEXT VALUE FOR dbo.seqExtraID;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Aqeel');

SELECT @ExtraID = NEXT VALUE FOR dbo.seqExtraID;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Rizwan');

SELECT @ExtraID = NEXT VALUE FOR dbo.seqExtraID;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Shariq');

SELECT @ExtraID = NEXT VALUE FOR dbo.seqExtraID;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Raheel');

SELECT @ExtraID = NEXT VALUE FOR dbo.seqExtraID;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Abu Bakar');

SELECT @ExtraID = NEXT VALUE FOR dbo.seqExtraID;
INSERT INTO tblTempTable (ExtraID, TempName) VALUES (@ExtraID, 'Waseem');

You may note that this is the same as how we addressed the issue in Solution #1. The only difference is that instead of selecting the value using the SELECT statement, we are retrieving the value from the SEQUENCE object.

Code can also be found in .SQL file by the name "04 - Using the Sequence Object.sql".

Here comes the final solution that will give us the same look and feel that we have two columns with IDENTITY in a single Table.

Solution #4: DEFAULT SEQUENCE in Table

We are familiar with the DEFAULT object. DEFAULT object specifies a value to be inserted into the column to which the object is bound when no value is explicitly supplied during an insert (for more details, see point 2 in references).

So first, we create a SEQUENCE. The code is as follows:

SQL
CREATE SEQUENCE dbo.seqExtraID AS INT START WITH 2 INCREMENT BY 2;

Now when we have the SEQUENCE by the name dbo.seqExtraID, we create the table with default value for Column ExtraID using the SEQUENCE object dbo.seqExtraID we just created! The code to create the table is as follows:

SQL
CREATE TABLE tblTempTable (
	ID INT PRIMARY KEY IDENTITY(1, 1),
	ExtraID INT NOT NULL DEFAULT _
	(NEXT VALUE FOR dbo.seqExtraID), -- NEED TO IMPLEMENT IDENTITY ON THIS COLUMN
	TempName VARCHAR(100)
);

Note how we mentioned the default value for ExtraID Column using the DETAULT keyword. Once the table is created, we can insert the data in the Table using the simple INSERT statement. The INSERT statements are as follows:

SQL
INSERT INTO tblTempTable (TempName) VALUES ('Aqeel');
INSERT INTO tblTempTable (TempName) VALUES ('Rizwan');
INSERT INTO tblTempTable (TempName) VALUES ('Shariq');
INSERT INTO tblTempTable (TempName) VALUES ('Raheel');
INSERT INTO tblTempTable (TempName) VALUES ('Abu Bakar');
INSERT INTO tblTempTable (TempName) VALUES ('Waseem');

SELECT * FROM tblTempTable;

When the above statements are executed, SQL Server will provide the results for the SELECT Statement. The results look like this:

Image 1

You may note that neither is there a need to mention the ExtraID Column now is it needed to provide its value. The INSERT statement executes just like we have not just one Column but two Columns with IDENTITY in a single Table.

Code can also be found in .SQL file by the name "05 - Using the Sequence Object in Table.sql".

Points of Interest

Illusion that a Table has not one but two columns with IDENTITY.

References

  1. https://msdn.microsoft.com/en-us/library/ff878058.aspx
  2. https://msdn.microsoft.com/en-us/library/ms173565.aspx

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)