You are on page 1of 5

orderid INT NOT NULL IDENTITY(1, 1) CONSTRAINT PK_MyOrders_orderid PRIMARY KEY, custid INT NOT NULL CONSTRAINT CHK_MyOrders_custid

CHECK(custid > 0), empid INT NOT NULL CONSTRAINT CHK_MyOrders_empid CHECK(empid > 0), orderdate DATE NOT NULL ); When you insert rows into the table, don t specify a value for the IDENTITY column because it gets its values automatically. For example, the following code inserts three rows and does not specify values for the orderid column. INSERT INTO Sales.MyOrders(custid, empid, orderdate) VALUES (1, 2, '20120620'), (1, 3, '20120620'), (2, 2, '20120620'); SELECT * FROM Sales.MyOrders; After the insertion, the query returns the following output. orderid custid empid orderdate ----------- ---------- ----------- ---------1 1 2 2012-06-20 2 1 3 2012-06-20 3 2 2 2012-06-20 For cases in which you insert rows into the table and you want to specify your o wn values for the orderid column, you need to set a session option called SET IDENTITY_INS ERT <table> to ON. Note that there s no option that you can set to update an IDENTITY column. T-SQL provides a number of functions that you can use to query the last identity value generated for example, in case you need it when you insert related rows into anoth er table: The SCOPE_IDENTITY function returns the last identity value generated in your ses sion in the current scope. The @@IDENTITY function returns the last identity value generated in your session regardless of scope. The IDENT_CURRENT function accepts a table as input and returns the last identity value generated in the input table regardless of session. As an example, the following code queries all three functions in the same sessio n that ran the last INSERT statement. SELECT SCOPE_IDENTITY() AS SCOPE_IDENTITY, @@IDENTITY AS [@@IDENTITY], IDENT_CURRENT('Sales.MyOrders') AS IDENT_CURRENT; 372 Chapter 11 Other Data Modification Aspects There was no activity after the last INSERT statement, either in the current ses sion or in others; hence, all three functions return the same values. SCOPE_IDENTITY @@IDENTITY IDENT_CURRENT --------------- ----------- -------------3 3 3 Next, open a new query window and run the query again. This time, you get the fo llowing result. SCOPE_IDENTITY @@IDENTITY IDENT_CURRENT --------------- ----------- -------------NULL NULL 3

Because you re issuing the query in a different session than the one that generate d the identity value, both SCOPE_IDENTITY and @@IDENTITY return NULLs. As for IDEN T_ CURRENT, it returns the last value generated in the input table irrespective of session. As for the difference between SCOPE_IDENTITY and @@IDENTITY, suppose that you ha ve a stored procedure P1 with three statements: An INSERT that generates a new identity value A call to a stored procedure P2 that also has an INSERT statement that generates a new identity value A statement that queries the functions SCOPE_IDENTITY and @@IDENTITY The SCOPE_IDENTITY function will return the value generated by P1 (same session and scope). The @@IDENTITY function will return the value generated by P2 (same sess ion irrespective of scope). If you need to delete all rows from a table, you should be aware of a specific d ifference between doing so by using a DELETE without a WHERE clause versus by using a TRUN CATE statement. The former doesn t affect the current identity value, whereas the latte r resets it to the initial seed. For example, run the following code to clear the Sales.MyOr ders table by using a TRUNCATE statement. TRUNCATE TABLE Sales.MyOrders; Next, query the current identity value in the table. SELECT IDENT_CURRENT('Sales.MyOrders') AS [IDENT_CURRENT]; You get 1 as the result. To reseed the current identity value, use the DBCC CHEC KIDENT command, as follows. DBCC CHECKIDENT('Sales.MyOrders', RESEED, 4); To see that the value was reseeded, issue an INSERT statement and query the tabl e. INSERT INTO Sales.MyOrders(custid, empid, orderdate) VALUES(2, 2, '20120620'); SELECT * FROM Sales.MyOrders; Lesson 1: Using the Sequence Object and IDENTITY Column Property Chapter 11 373 You get the following output. orderid custid empid orderdate ----------- ---------- ----------- ---------4 2 2 2012-06-20 It is important to understand that there are certain things that the IDENTITY pr operty doesn t guarantee. It doesn t guarantee uniqueness. Remember that you can enter expl icit values if you turn on the IDENTITY_INSERT option. Also, you can reseed the prope rty value. To guarantee uniqueness you must use a constraint like PRIMARY KEY or UNIQUE. Also, the IDENTITY property doesn t guarantee that there will be no gaps between t he values. If an INSERT statement fails, the current identity value is not changed back to the original one, so the unused value is lost. The next insertion will have a value after the one that wasn t used. To demonstrate this, run the following INSERT statement. INSERT INTO Sales.MyOrders(custid, empid, orderdate) VALUES(3, -1, '20120620'); This statement violates the CHECK constraint defined on the table with the predi

cate empid > 0. This code generates the following error. Msg 547, Level 16, State 0, Line 1 The INSERT statement conflicted with the CHECK constraint "CHK_MyOrders_empid". The conflict occurred in database "TSQL2012", table "Sales.MyOrders", column 'empid' . The statement has been terminated. The IDENTITY property generated a new identity value of 5 for the INSERT stateme nt; however, SQL Server did not undo the change to the current identity value due to the failure. Next, issue another INSERT statement. INSERT INTO Sales.MyOrders(custid, empid, orderdate) VALUES(3, 1, '20120620'); This time, the insertion succeeds. Query the table. SELECT * FROM Sales.MyOrders; You get the following output. orderid custid empid orderdate ----------- ---------- ----------- ---------4 2 2 2012-06-20 6 3 1 2012-06-20 Observe that the value 5 that was generated for the failed INSERT statement wasn t used and now you have a gap between the values. For this reason, the IDENTITY column property is not an adequate sequencing solution in cases where you cannot allow gaps. One su ch example is for invoicing systems; gaps between invoice numbers are not allowed. In such cases, you need to create an alternative solution, such as storing the last-used value in a table. The IDENTITY property has no cycling support. This means that after you reach th e maximum value in the type, the next insertion will fail due to an overflow error. To get around this, you need to reseed the current identity value before such an attempt is made. 374 Chapter 11 Other Data Modification Aspects Using the Sequence Object SQL Server 2012 introduces the sequence object. Unlike the IDENTITY column prope rty, a sequence is an independent object in the database. The sequence object doesn t suf fer from many of the limitations of the IDENTITY property, which include the following: The IDENTITY property is tied to a particular column in a particular table. You c annot remove an existing property from a column or add it to an existing column. The column has to be defined with the property. Sometimes you need keys to not conflict across different tables. But IDENTITY is tablespecific. Sometimes you need to generate the value before using it. With the IDENTITY prope rty, this is not possible. You have to insert the row and only then collect the new v alue with a function. You cannot update an IDENTITY column. The IDENTITY property doesn t support cycling. A TRUNCATE statement resets the identity value. The sequence object doesn t suffer from these limitations. This section explains h ow to work with the object and shows how it doesn t suffer from the same restrictions as

IDENTITY. You create a sequence object as an independent object in the database. It s not ti ed to a particular column in a particular table. You use the CREATE SEQUENCE command to create a sequence. At a minimum, you just need to specify a name for the object, as follo ws. CREATE SEQUENCE <schema>.<object>; Like IDENTITY, all numeric types with a scale of 0 are supported. But if you don t indicate a type explicitly, SQL Server will assume BIGINT by default. If you need a diffe rent type, you need to ask for it explicitly by adding AS <type> after the sequence name. There are a number of properties that you can set, all with default options in c ase you don t provide your own. The following are some of the properties and their default values: INCRE MENT BY Increment value. The default is 1. MINVA LUE The minimum value to support. The default is the minimum value in the type. For example, for an INT type, it will be -2147483648. MAXVALUE The maximum value to support. The default is the maximum value in the type. CYC LE | NO CYC LE Defines whether to allow the sequence to cycle or not. The def ault is NO CYCLE. START WITH The sequence start value. The default is MINVALUE for an ascending sequence (positive increment) and MAXVALUE for a descending one. Lesson 1: Using the Sequence Object and IDENTITY Column Property Chapter 11 375 Here s an example you can use to define a sequence that will help generate order I Ds. CREATE SEQUENCE Sales.SeqOrderIDs AS INT MINVALUE 1 CYCLE; Observe that the definition sets the minimum value to 1 (and therefore also the start value to 1), and specifies that the sequence should allow cycling. The more common thi ng to see people doing when they want the sequence to start with 1 is to set the START WIT H property to 1. However, this won t change the minimum value from -2147483648. This will tur n out to be a problem if the sequence allows cycling. After you hit the last value in the type, the next value generated won t be 1; instead, it will be -2147483648. Therefore, the smarte r thing to do if you need your sequence to generate only positive values is to set the MINV ALUE to 1. This will implicitly set the START WITH value to 1 as well. Note that in real-life cases, normally you would not allow a sequence generating order IDs to cycle, but here the cycling sequence is defined just for demonstration purpos es. To request a new value from the sequence, use the NEXT VALUE FOR <sequence name> function. For example, run the following code three times. SELECT NEXT VALUE FOR Sales.SeqOrderIDs; You get the values 1, 2, and 3. This function can be called in INSERT VALUES and INSERT SELECT statements, a SET clause of an UPDATE statement, an assignment to a varia ble, a DEFAULT constraint expression, and other places. Examples are provided later in

this lesson. You cannot change the data type of an existing sequence, but you can change all of its properties by using the ALTER SEQUENCE command. For example, if you want to chan ge the current value, you can do so with the following code. ALTER SEQUENCE Sales.SeqOrderIDs RESTART WITH 1; To see for yourself how to use sequence values when inserting rows into a table, recreate the Sales.MyOrders table by running the following code. IF OBJECT_ID('Sales.MyOrders') IS NOT NULL DROP TABLE Sales.MyOrders; GO CREATE TABLE Sales.MyOrders ( orderid INT NOT NULL CONSTRAINT PK_MyOrders_orderid PRIMARY KEY, custid INT NOT NULL CONSTRAINT CHK_MyOrders_custid CHECK(custid > 0), empid INT NOT NULL CONSTRAINT CHK_MyOrders_empid CHECK(empid > 0), orderdate DATE NOT NULL ); 376 Chapter 11 Other Data Modification Aspects Observe that this time the orderid column doesn t have an IDENTITY property. Here s an example of using the NEXT VALUE FOR function in an INSERT VALUES stateme nt that inserts three rows into the table. INSERT INTO Sales.MyOrders(orderid, custid, empid, orderdate) VALUES (NEXT VALUE FOR Sales.SeqOrderIDs, 1, 2, '20120620'), (NEXT VALUE FOR Sales.SeqOrderIDs, 1, 3, '20120620'), (NEXT VALUE FOR Sales.SeqOrderIDs, 2, 2, '20120620'); As mentioned, you can also use the function in INSERT SELECT statements. In such a case, you can optionally add an OVER clause with an ORDER BY list to control the order in which the sequence values are assigned to the result rows. INSERT INTO Sales.MyOrders(orderid, custid, empid, orderdate) SELECT NEXT VALUE FOR Sales.SeqOrderIDs OVER(ORDER BY orderid), custid, empid, orderdate FROM Sales.Orders WHERE custid = 1; This is a T-SQL extension to the standard. To see the values generated by both statements, query the table. SELECT * FROM Sales.MyOrders; You get the following output. orderid custid