Tag Archives: Series

Articles that are too big to fit into one part.

#0268 – SQL Server – IDENTITY columns – Myths – Values cannot explicitly inserted into IDENTITY columns


A myth around IDENTITY columns is that values cannot be explicitly inserted into IDENTITY columns. This myth may be coming from the fact that IDENTITY columns compute their values automatically when the INSERT operation is performed and that by default, attempting to insert explicit values into an IDENTITY column throws us an error with Msg #544:

USE tempdb;
GO

SET NOCOUNT ON;

--1. Prepare the environment
--   Create a table, and add some test data into it

--Safety Check
IF OBJECT_ID('tIdentity','U') IS NOT NULL
    DROP TABLE dbo.tIdentity;
GO

--Create a table
CREATE TABLE dbo.tIdentity (IdentityId INT IDENTITY(1,1),
                            IdentityValue VARCHAR(10)
                           );
GO

--Add some test data
INSERT INTO dbo.tIdentity (IdentityValue)
VALUES ('One'),
       ('Two'),
       ('Three');
GO

--2. Check the current identity value of the table using IDENT_CURRENT()
SELECT IDENT_CURRENT('dbo.tIdentity') AS IdentityValueBeforeTest;
GO

--3. Confirm that explicitly inserting data into the IDENTITY column results 
--   into an error
INSERT INTO dbo.tIdentity (IdentityId, IdentityValue)
VALUES (5, 'Five');
GO

/*
Msg 544, Level 16, State 1, Line 4
Cannot insert explicit value for identity column in table 'tIdentity' when IDENTITY_INSERT is set to OFF.
*/

Msg 544, Level 16, State 1, Line 4
Cannot insert explicit value for identity column in table ‘tIdentity’ when IDENTITY_INSERT is set to OFF.


Busting the Myth – Explicit values cannot be inserted into IDENTITY columns


Only one question is sufficient to challenge this myth – How should the system work when data is being moved from one table to another in case of replication or in case of a data migration?


The answer to this question comes as a simple SET option – SET IDENTITY_INSERT. This SET option allows a user to explicitly specify the values for the IDENTITY column. The script below provides a quick demonstration for usage of this option. This script:



  1. Sets the IDENTITY_INSERT property to ON

  2. Inserts two records with an explicit value for the IdentityId (IDENTITY column)

  3. Checks the new value of the identity property using the IDENT_CURRENT function

USE tempdb;
GO

SET NOCOUNT ON;

--Assumption: Query #06 has already been executed
SET IDENTITY_INSERT dbo.tIdentity ON;
GO

INSERT INTO dbo.tIdentity (IdentityId, IdentityValue)
VALUES (6, 'Six'),
       (7, 'Seven');
GO

--6. Select the values from the table
SELECT ti.IdentityId, ti.IdentityValue
FROM dbo.tIdentity AS ti;
GO

SELECT IDENT_CURRENT('dbo.tIdentity') AS IdentityValueAfterTest;
GO

--Cleanup
SET IDENTITY_INSERT dbo.tIdentity OFF;
GO
IF OBJECT_ID('tIdentity','U') IS NOT NULL
    DROP TABLE dbo.tIdentity;
GO

/**********************************************
               RESULTS
**********************************************/
/*
IdentityId  IdentityValue
----------- -------------
1           One
2           Two
3           Three
6           Six
7           Seven

IdentityValueAfterTest
----------------------
7

*/

Please NOTE however that one needs to turn OFF IDENTIYTY_INSERT for normal operation of IDENTITY columns to be resumed.


As can be seen from the script above, explicitly inserting values into an Identity column causes the system to increment the internal identity counter also.


Conclusion


The test shown above clearly proves that it is possible to explicitly insert values into an IDENTITY column. This option (IDENTITY_INSERT) is very useful when working on data migration packages where it allows to pump data from one table to another “as-is”.


Until we meet next time,


Be courteous. Drive responsibly.

#0267 – SQL Server – IDENTITY columns – Myths – IDENTITY columns cannot have holes or “gaps”


Whenever a pattern evolves into a frequently used practice, it does not take long for myths to start getting associated with it. IDENTITY columns have become such a common pattern that often I hear about these myths from novice developers. Using this series, I hope to bust those myths once and for all.


Busting the Myth – IDENTITY columns cannot have holes


Whenever a break in a series is encountered, it is referred to as a hole. For example, if we have a series of numbers from 1 to 5 such that the number 2 is missing (1, 3, 4, 5), then we have a hole.


I often hear from developers that “Holes cannot be generated in the values of a column defined as the IDENTITY column” is a prime reason why one uses IDENTITY columns. This is in-fact incorrect as I will be proving by this post.


The Test


To provide that holes can be generated in an IDENTITY column, I will use the script shown below. The script:



  1. Prepares the environment by creating a table and adding some test data into it

  2. Check the current identity value of the table using IDENT_CURRENT()

  3. Opens a transaction

  4. Performs an insert into a table which has the IDENTITY property defined

  5. Later, the transaction is rolled back, and the current identity value of the table is queried using IDENT_CURRENT()

  6. To confirm, a record is inserted into the table and the values selected – clearly indicating the presence of the hole

USE tempdb;
GO

SET NOCOUNT ON;

--1. Prepare the environment
--   Create a table, and add some test data into it

--Safety Check
IF OBJECT_ID('tIdentity','U') IS NOT NULL
    DROP TABLE dbo.tIdentity;
GO

--Create a table
CREATE TABLE dbo.tIdentity (IdentityId INT IDENTITY(1,1),
                            IdentityValue VARCHAR(10)
                           );
GO

--Add some test data
INSERT INTO dbo.tIdentity (IdentityValue)
VALUES ('One'),
       ('Two'),
       ('Three');
GO

--2. Check the current identity value of the table using IDENT_CURRENT()
SELECT IDENT_CURRENT('dbo.tIdentity') AS IdentityValueBeforeTest;
GO

--3. Perform the test
--3a. Open a transaction
BEGIN TRANSACTION IdentityHolesCheck
    --3b. Perform an insert
    INSERT INTO dbo.tIdentity (IdentityValue)
    VALUES ('Four');
--3c. Rollback the transaction
ROLLBACK TRANSACTION IdentityHolesCheck;
GO

--4. Check the current identity value of the table using IDENT_CURRENT()
SELECT IDENT_CURRENT('dbo.tIdentity') AS IdentityValueAfterTest;
GO

--5. Confirmatory test
INSERT INTO dbo.tIdentity (IdentityValue)
VALUES ('Five');
GO

--6. Select the values from the table
SELECT ti.IdentityId, ti.IdentityValue
FROM dbo.tIdentity AS ti;
GO

--Cleanup
IF OBJECT_ID('tIdentity','U') IS NOT NULL
    DROP TABLE dbo.tIdentity;
GO

/**********************************************
               RESULTS
**********************************************/
/*
IdentityValueBeforeTest
-----------------------
3

IdentityValueAfterTest
----------------------
4

IdentityId  IdentityValue
----------- -------------
1           One
2           Two
3           Three
5           Five
*/

While this was a simulation, in a practical world this situation is similar to a failed insert. Even if the INSERT did not go through, the IDNETITY value was incremented leaving the hole behind.


Conclusion


The test shown above clearly proves that it is possible to have holes in an IDENTITY column. Any code that expects an uninterrupted series of values may break if this has not been taken into consideration.


Until we meet next time,


Be courteous. Drive responsibly.

#0266 – SQL Server – IDENTITY Columns – @@IDENTITY, SCOPE_IDENTITY, IDENT_CURRENT and $IDENTITY


IDENTITY columns are perhaps the least understood and yet most used features of Microsoft SQL Server.


In this post, I will explore some system functions and keywords that we as developers and administrators can use to get information about IDENTITY columns.


Whenever a record is inserted to a table using an IDENTITY column via an INSERT, SELECT INTO or bulk copy command, the identity values on the affected tables are incremented. Microsoft SQL Server gives us three system functions that can provide the last used identity value – depending upon the scope and session.


IDENT_CURRENT()


Given a table name, this system function returns the last identity value used for the given table or view. Since this function works on a particular table or view, this value is independent of the session or the scope.


SCOPE_IDENTITY()


This system function provides the last used IDENTITY value within the same scope.


Statements inside the same stored procedure, trigger or batch comprise of the same scope – those within triggers fired as a result of a statement inside a stored procedure aren’t.


If no statement has been executed which affects an IDENTITY value, this function returns NULL.


@@IDENTITY


The @@IDENTITY system function contains the last value that was generated by the statement – irrespective of the scope.


If no statement has been executed which affects an IDENTITY value, this function returns NULL. However, please note that this function reports the identity value last generated by the statement – irrespective of the scope. This means that if a statement causes execution of a trigger which generates identity values in other tables, calling the @@IDENTITY function immediately after this statement will return the last IDENTITY value generated by the trigger.


Let’s look at each of these via an example:

USE AdventureWorks2008R2;
GO
SET NOCOUNT ON ;

SELECT  IDENT_CURRENT('Purchasing.PurchaseOrderDetail') AS PODetailIdentity,
        IDENT_CURRENT('Production.TransactionHistory') AS TransactionlIdentity,
        SCOPE_IDENTITY() AS ScopeIdentityValue,
        @@IDENTITY AS LastUsedIdentity;

    --Perform the INSERT into the table: Purchasing.PurchaseOrderDetail
    --The insert trigger on this table: Purchasing.iPurchaseOrderDetail, causes a record
    --to be inserted into Production.TransactionHistory, thereby incrementing it's IDENTITY column - TransactionID
    INSERT  INTO Purchasing.PurchaseOrderDetail
            (
              PurchaseOrderID,
              DueDate,
              OrderQty,
              ProductID,
              UnitPrice,
             --LineTotal,
              ReceivedQty,
              RejectedQty,
             --StockedQty,
              ModifiedDate
            )
            SELECT  1 AS PurchaseOrderId,
                    '2005-05-31' AS DueDate,
                    1 AS OrderQty,
                    4 AS ProductId,
                    57.02 AS UnitPrice,
                    --258.06 AS LineTotal,
                    1 AS ReceivedQty,
                    0 AS RejectedQty,
                    --1 AS StockedQty,
                    '2013-05-09' AS ModifedDate ;
    GO

SELECT  IDENT_CURRENT('Purchasing.PurchaseOrderDetail') AS PODetailIdentity,
        IDENT_CURRENT('Production.TransactionHistory') AS TransactionlIdentity,
        SCOPE_IDENTITY() AS ScopeIdentityValue,
        @@IDENTITY AS LastUsedIdentity;

/****RESULTS****/
/*
        PODetailIdentity    TransactionlIdentity    ScopeIdentityValue    LastUsedIdentity
Before: 8847                213450                  NULL                  NULL  
After : 8848                213451                  8848                  213451
*/

Inserting a new record in the table: Purchasing.PurchaseOrderDetail causes the insert trigger on this table: Purchasing.iPurchaseOrderDetail to be executed. This trigger maintains a history of the PurchaseOrder Detail transactions in the table – Production.TransactionHistory, thereby incrementing it’s IDENTITY column – TransactionID.


As can be seen in the output:



  • IDENT_CURRENT() provides us the last used identity value for the given table

  • SCOPE_IDENTITY() provides us the last used identity value within the scope of the batch

  • @@IDENTITY provides us the last used identity value irrespective of the scope

$IDENTITY


The $IDENTITY keyword allows us to refer to an identity column in a table without using the corresponding column name. Here’s an example:

USE AdventureWorks2008R2 ;
GO
SELECT  $IDENTITY
FROM    Purchasing.PurchaseOrderDetail ;
GO

The query above will return the value of the [PurchaseOrderDetailId] in the table [Purchasing].[PurchaseOrderDetail] since it’s marked as an identity column.


Other functions – IDENT_INCR() and IDENT_SEED()


If one needs to look at the value of the seed and increment values used in the identity column definition, the IDENT_INCR() and IDENT_SEED() functions can be used.

USE AdventureWorks2008R2;
GO
SELECT IDENT_SEED('Production.TransactionHistory') AS IdentityIncrement,
       IDENT_INCR('Production.TransactionHistory') AS IdentitySeed;
GO
/* RESULTS */
/*
IdentityIncrement   IdentitySeed
100000              1
*/

Checking for Identity columns in a table


The Catalog View sys.identity_columns has one record for each identity column and provides information like the seed, increment, last value generated for that column and other meta-data information.


Usage is shown in the query below:

USE AdventureWorks2008R2 ;
GO
SELECT * FROM sys.identity_columns
WHERE object_id = OBJECT_ID('Production.TransactionHistory');
GO

Until we meet next time,


Be courteous. Drive responsibly.

Getting started with Contained Databases in SQL 11 (“Denali”) – Part 01 – An Introduction


As you know, I had visited Tech-Ed 2011 (India) last month. The last day of Tech-Ed was a special day for DBAs because we had a dedicated DBA track – and to add to it, we had a power packed hands-on lab from Jacob Sebastian and break-out sessions from Pinal Dave, Vinod Kumar, Prem Mehra and Balmukund Lakhani. You can read all about Tech-Ed Day 03 at my post here.

For this week, I will be sharing with you the content of the hands-on lab on “Contained Databases in SQL 11 (“Denali”)” from Tech Ed. The hands-on lab was conducted by Jacob Sebastian.

Database containment – the current situation

What does a typical day in the life of the DBA (especially in a development shop) consist of? Let me list a few of these activities down:

  1. Create new databases
  2. Setup database options
  3. Create Logins on the SQL Server and assign permissions to various objects
  4. Move databases across SQL Servers
  5. Repeat the same steps on demand

SQL Server is huge, and there are a variety of objects and tasks to be done within the server. Unfortunately, these objects and tasks are spread all over the entire product, which leads to the following disadvantages:

Database Deployment

This is the most feared situation – it is the time when an application is moved from one instance to another. When this movement happens, some part of the application’s data (eg. the logins and agent job information) is left behind.

The DBA must then painstakingly recreate and map each login to required database users on the new instance. Maintenance and other SQL Agent jobs also need to be recreated on the new instance. Depending upon the number of such operations to be carried out, this process is time-consuming and error-prone.

Application Development

Application development has to currently keep into consideration the finer points of the final deployment/production environment. Database and server configuration and deployment scenarios are no longer isolated. It is quite possible that some of the dependencies and conditions that application developers assumed to be available may not be true (eg. the availability of the “sa” login, permissions to create new logins on the server, or “xp_cmdshell” being enabled).

Another major influencer in application development is the collation of the database. Since this is initially determined by the server collation, developers need to constantly ensure that the collation is exactly as required by the application.

Such situations take the focus away from application development and instead divert it to application deployment, which should not be the case.

Application Administration

Because logins and agent jobs are spread across the instance and the database, administration is a virtual nightmare. In high-security enterprise class installations, the need to administer a single database typically requires that the user be granted permissions to the entire instance – thus providing access to many other databases to which the user has no relation with.

Contained Databases – The solution

There exists a distinct boundary between the multiple areas of any application:

  1. Application boundary – the boundary between the application code and functionality and the server instance
  2. Application Model – inside the application boundary, this is where applications are developed and managed
  3. Management Model – outside of the application boundary, this is where instance-level management happens

Here is how you may classify some of the objects into Application model & Management model:

Application Model Management Model
System tables like sys.tables Instance level system tables like sys.endpoints
Database users and passwords Database server logins
Database server login and database user mapping
User tables in the current database
(i.e. referenced by a 2-part name)
User tables in another database
(i.e. referenced by a 3-part name)

Once these boundaries are defined, and the objects completely contained within these boundaries, they will become independent of the objects on the other side of the boundary.

A “Contained Database” paradigm ensures:

  1. Simplified database movement
  2. Application development environment independent of the deployment environment considerations
  3. More granular, yet powerful administrative groups possible

SQL 11 (“Denali”) and Contained Databases

As of SQL 11 (“Denali”) CTP 01, SQL Server supports the concept of “Contained databases”. Here’s how you can define contained database:

“A contained database has no external dependencies on server-based settings or metadata to define the database. Users defined in a contained database do not have a dependency on logins created on the server, and therefore contain all information necessary to authenticate a user within the database.”

By default, this feature is turned OFF, meaning everything is just as it was before, i.e. “uncontained”.

Tomorrow, we will soon see how to turn Contained Database support ON, and also create a new contained database, after which we will see how to migrate a contained database across severs, and finally, how to convert your non-contained database to a contained one.

You can understand more about Contained Databases at: http://msdn.microsoft.com/en-us/library/ff929071(SQL.110).aspx

Until tomorrow,

Be courteous. Drive responsibly.