SQL Server 2011 (code named Denali) CTP1 was announced in November 2010 during the SQL PASS Summit in Seattle. While a bit disappointing not to see the much anticipated full implementation of the window functions (hope we will still see that in a future CTP version), it offers some interesting new programmability features. These new enhancements address specific problems that we see very often in business applications. Here is a quick look at the key new features in T-SQL.
Feature: OFFSET 
Application use: paging 
Comments: provides simplified syntax and efficient method for data paging solutions
| Listing 1: OFFSET example | 
CREATE TABLE Customers (  customer_nbr INT NOT NULL PRIMARY KEY,  customer_name VARCHAR(35) NOT NULL);INSERT INTO Customers  VALUES  (1, 'Joe'),  (2, 'John'),  (3, 'Jane'),  (4, 'Peter'),  (5, 'Mary'),  (6, 'Jose'),  (7, 'Daniel'),  (8, 'Adam'),  (9, 'Chris'),  (10, 'Tom'),  (11, 'Evan'),  (12, 'Lora');SELECT customer_name  FROM Customers  ORDER BY customer_name;/*customer_name  ---------------  Adam  Chris  Daniel  Evan  Jane  Joe  John  Jose  Lora  Mary  Peter  Tom */ DECLARE @page_nbr INT = 1, @page_size INT = 5;-- first page  SELECT customer_name  FROM Customers  ORDER BY customer_name  OFFSET (@page_nbr - 1) * @page_size ROWS FETCH NEXT @page_size ROWS ONLY;/*customer_name  ---------------  Adam  Chris  Daniel  Evan  Jane */ -- second page 
  SET @page_nbr = 2;SELECT customer_name  FROM Customers  ORDER BY customer_name  OFFSET (@page_nbr - 1) * @page_size ROWS FETCH NEXT @page_size ROWS ONLY;/*customer_name  ---------------  Joe  John  Jose  Lora  Mary */ -- last page 
  SELECT customer_name  FROM Customers  ORDER BY customer_name  OFFSET ((SELECT COUNT(*) FROM Customers) / @page_size) * @page_size  ROWS FETCH NEXT @page_size ROWS ONLY;/*customer_name  ---------------  Peter  Tom */ -- any 3 customers 
  SELECT customer_name  FROM Customers  ORDER BY (SELECT NULL)  OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY;/*customer_name  ---------------  Joe  John  Jane */ DROP TABLE Customers;  | 
Feature: THROW 
Application use: error handling 
Comments: allow to re-throw the original error
| Listing 2: THROW example | 
BEGIN TRY       -- inside code THROW is similar to RAISERROR with limitations   THROW 51000, 'User error.', 1;END TRYBEGIN CATCH        -- inside CATCH rethrow the error    THROW;     END CATCH | 
Feature: SEQUENCE 
Application use:  replacement for IDENTITY 
Comments: ANSI standard method for sequences, improves on shortcomings of IDENTITY
| Listing 3: SEQUENCE example | 
CREATE TABLE Customers ( customer_nbr INT NOT NULL PRIMARY KEY, customer_name VARCHAR(35) NOT NULL);-- create sequence starting with value 1, minimum value 1, -- no maximum value, and increment by 1 CREATE SEQUENCE CustomerNbr AS INT     MINVALUE 1     NO MAXVALUE     START WITH 1 INCREMENT BY 1;-- generate customer numbers based on the sequence -- using the NEXT VALUE FOR function INSERT INTO Customers (customer_nbr, customer_name) SELECT NEXT VALUE FOR CustomerNbr, 'Joe' UNION ALL SELECT NEXT VALUE FOR CustomerNbr, 'John' UNION ALL SELECT NEXT VALUE FOR CustomerNbr, 'Jane' UNION ALL SELECT NEXT VALUE FOR CustomerNbr, 'Peter' UNION ALL SELECT NEXT VALUE FOR CustomerNbr, 'Mary';SELECT customer_nbr, customer_name  FROM Customers ORDER BY customer_nbr;/*customer_nbr customer_name ------------ --------------- 1            Joe 2            John 3            Jane 4            Peter 5            Mary */ -- use OVER to generate next set of sequence numbers -- based on ordering by customer name
  INSERT INTO Customers (customer_nbr, customer_name) SELECT NEXT VALUE FOR CustomerNbr OVER(ORDER BY customer_name), customer_name FROM ( VALUES ('Jose'), ('Daniel'), ('Adam'), ('Chris'), ('Tom')) AS T(customer_name);SELECT customer_nbr, customer_name  FROM Customers ORDER BY customer_nbr;/*customer_nbr customer_name ------------ --------------- 1            Joe 2            John 3            Jane 4            Peter 5            Mary 6            Adam 7            Chris 8            Daniel 9            Jose 10           Tom */ -- alter sequence to set next number to 20 -- and increment by 10
  ALTER SEQUENCE CustomerNbr     RESTART WITH 20     INCREMENT BY 10;INSERT INTO Customers (customer_nbr, customer_name) SELECT NEXT VALUE FOR CustomerNbr, 'Evan' UNION ALL SELECT NEXT VALUE FOR CustomerNbr, 'Lora';SELECT customer_nbr, customer_name  FROM Customers ORDER BY customer_nbr;/*customer_nbr customer_name ------------ --------------- 1            Joe 2            John 3            Jane 4            Peter 5            Mary 6            Adam 7            Chris 8            Daniel 9            Jose 10           Tom 20           Evan 30           Lora */ -- reset sequence to 1
  ALTER SEQUENCE CustomerNbr     RESTART WITH 1     INCREMENT BY 1;DECLARE @first_value SQL_VARIANT, @last_value SQL_VARIANT;-- get a range of 5 sequence values: 1, 2, 3, 4, 5 -- next available value is 6 EXEC sp_sequence_get_range   @sequence_name = N'CustomerNbr',   @range_size = 5,   @range_first_value = @first_value OUTPUT,   @range_last_value = @last_value OUTPUT;SELECT @first_value AS range_first_value,         @last_value AS range_last_value;/*range_first_value   range_last_value ------------------- ------------------ 1                   5 */ SELECT NEXT VALUE FOR CustomerNbr AS next_value;/*next_value ----------- 6 */ DROP TABLE Customers; DROP SEQUENCE CustomerNbr; | 
Feature: EXECUTE WITH RESULT SETS 
Application use: manipulate stored procedure output result set 
Comments: capabilities to rename output result set columns without changing the original stored procedure; no options to remove/add columns or remove a result set when multiple result sets are returned
| Listing 4: EXECUTE WITH RESULT SETS example | 
CREATE PROCEDURE CalculateSales ASSELECT  sale_month, sale_amount FROM (VALUES('2010-01', 120.50),              ('2010-02', 214.00),              ('2010-03', 109.10)) AS T(sale_month, sale_amount);SELECT SUM(sale_amount) AS total_sales FROM (VALUES('2010-01', 120.50),              ('2010-02', 214.00),              ('2010-03', 109.10)) AS T(sale_month, sale_amount);GOEXECUTE CalculateSales;/*sale_month sale_amount ---------- ------------- 2010-01    120.50 2010-02    214.00 2010-03    109.10 (3 row(s) affected) total_sales ------------- 443.60 (1 row(s) affected) */ EXECUTE CalculateSales  WITH RESULT SETS  (     (month CHAR(7), amount DECIMAL(10, 2)),    ( total DECIMAL(10, 2)) );/*month   amount ------- -------- 2010-01 120.50 2010-02 214.00 2010-03 109.10 (3 row(s) affected) total -------- 443.60 (1 row(s) affected) */ DROP PROCEDURE CalculateSales; </br/> | 
Feature: describe result sets 
Application use: determining the format of a response without actually running the query 
Comments: replaces SET FMTONLY
| Listing 5: Describe result sets example | 
CREATE PROCEDURE CalculateSales ASSELECT  sale_month, sale_amount FROM (VALUES('2010-01', 120.50),              ('2010-02', 214.00),              ('2010-03', 109.10)) AS T(sale_month, sale_amount);SELECT SUM(sale_amount) AS total_sales FROM (VALUES('2010-01', 120.50),              ('2010-02', 214.00),              ('2010-03', 109.10)) AS T(sale_month, sale_amount);GO-- replacement for SET FMTONLY EXECUTE sp_describe_first_result_set N'CalculateSales';/*abbreviated results is_hidden column_ordinal name         system_type_name   --------- -------------- ------------ -----------------  0         1              sale_month   varchar(7)             0         2              sale_amount  numeric(5,2)           */ -- use DMV
  SELECT name, system_type_name  FROM sys.dm_exec_describe_first_result_set(N'CalculateSales', 1, 1);CREATE TABLE Foo ( keycol INT NOT NULL PRIMARY KEY, datacol CHAR(10));EXEC sp_describe_undeclared_parameters N'SELECT datacol FROM Foo WHERE keycol = @x';/*abbreviated results parameter_ordinal name  suggested_system_type_name   ----------------- ----- ----------------------------  1                 @x    int                           */ GODROP TABLE Foo;DROP PROCEDURE CalculateSales; </br/> | 
Bonus feature (maybe): FORMATMESSAGE 
Application use: format messages (C/C++ sprint style) 
Comments: undocumented feature allows to format message that is not in sys.messages
| Listing 6: FORMATMESSAGE example | 
SELECT FORMATMESSAGE('There are %d products in department %s.', 10, 'remodeling');/*There are 10 products in department remodeling. */  </br/> |