Sunteți pe pagina 1din 4

SQL Server 2014 brings us a TSQL improvement called inline specification of CLUST ERED and NONCLUSTERED indexes.

This feature works in Standard Edition and applies to many types of table. This improves the functionality of table variables. But does it make table variables as good as temp tables? First, let s take a look at the feature, then take it for a test drive. CREATING A TABLE WITH AN INLINE NONCLUSTERED INDEX Here s a simple look at the new feature when I create a table (nothing temporary ab out it), I can name and define an index on multiple columns. In this case, I m cre ating a nonclustered index on the columns j and k: CREATE TABLE dbo.IndexCreation ( i int identity constraint pkIndexCreation primary key clustered, j char(10) index ixIndexCreation_jk nonclustered (j,k), k varchar(100) ); GO I also have the option to put all my constraint and index create statements at t he end of the table, like this: CREATE TABLE dbo.IndexCreation ( i int identity, j char(10), k varchar(100), constraint pkIndexCreation primary key clustered (i), index ixIndexCreation_jk nonclustered (j,k) ); GO WHAT ABOUT TEMP TABLES? The same syntax for inline index create listed above works just fine for me on t emporary tables in my CTP of SQL Server 2014. This is a good thing for some people! One of the issues with temporary tables in stored procedures is that creating an index on the temp table can prevent cachi ng of the temporary item. For most people, this is no big deal, but for some fre quently run stored procedures, it might make a difference. Creating the nonclustered indexes on the temp table at creation time (instead of afterward) can improve temp table caching in stored procedures. Before you rejoice, there s some fine print. If you change your temp table syntax to use inline index creation and it enables caching, you might run into issues w here statistics aren t updated on your temporary tables when you think they would be. (The short version: Statistics are also cached for temp tables, and Gremlins keep from updating very frequently.) Just test carefully if you ve got very frequ ently run stored procedures you re modifying. INLINE INDEX CREATION ON TABLE VARIABLES The new syntax works on table variables, too! This means that with SQL Server 20 14 and higher, you can create non-unique nonclustered indexes on table variables . You can even set the fillfactor option. (Note: I m not saying you should use a l ower fillfactor I was just surprised that option was available for table variable s.)

DECLARE @IndexCreation TABLE ( i int identity primary key clustered with (fillfactor=95), j char(10) index ixIndexCreation_jk nonclustered (j,k) with (fillfactor=95), k varchar(100) ); GO With the new SQL 2014 syntax, you will also have the option to create a non-uniq ue clustered index on a table variable: DECLARE @IndexCreation TABLE ( i int, j char(10), k varchar(100), index cxIndexCreation_i clustered (i), index ixIndexCreation_jk nonclustered (j,k) ); GO SO, ARE TABLE VARIABLES AS GOOD AS TEMP TABLES NOW? Well, sorry, not generally. First, there s some limitations to very kind of index you might want exes created with an inline create se to a temporary table after it s riable), it has an advantage. the inline index create feature. You can t inline e you can t add included columns or filters to ind statement. Since you can add indexes with tho created (and you CANNOT do that with a table va

But temporary tables still have another advantage. STATISTICS HELP TEMPORARY TABLES When looking at the new feature, I wondered if these new indexes declared inline might secretly allow some sort of populated statistic on table variables which h asn t worked before. But, unfortunately no. Even using the inline indexes, table variables do NOT get statistics to help the optimizer psychically predict how many rows will come ba ck based on a specific predicate value. That can cause you to get less efficient execution plans. Let s look at an example. First up, the temp table: CREATE TABLE #TestMe ( i INT IDENTITY, City VARCHAR(60), StateProvinceID INT, INDEX ixTestMe_StateProvinceID NONCLUSTERED (StateProvinceID), CONSTRAINT cxTestMe_i PRIMARY KEY CLUSTERED (i) ); INSERT #TestMe (City,StateProvinceID) SELECT City, StateProvinceID FROM AdventureWorks2012.Person.Address; GO --This gets a nested loop plan SELECT City FROM #TestMe WHERE StateProvinceID=1; GO;

For the temporary table, SQL Server uses statistics associated with the nonclust ered index to estimate that it will get 25 rows back (which is right on). Based on this it decides to seek to the rows in the nonclustered index, then do a nest ed loop lookup to fetch the City column from the clustered index. It does 52 log ical reads: Key Lookup Now let s run the same code, but with a table variable with an inline index: DECLARE @TestMe TABLE ( i INT IDENTITY PRIMARY KEY CLUSTERED (i), City VARCHAR(60), StateProvinceID INT, INDEX ixTestMe_StateProvinceID NONCLUSTERED (StateProvinceID) ); INSERT @TestMe (City,StateProvinceID) SELECT City, StateProvinceID FROM AdventureWorks2012.Person.Address; --This gets a clustered index scan SELECT City FROM @TestMe WHERE StateProvinceID=1; GO Oddly enough, it gets a clustered index scan. It estimates that only one row wil l be found that s because for table variables, statistics associated with the nonc lustered index still can t be populated. So it doesn t know to estimate the 25 rows, and it just guesses one. With a one row estimate, I thought the optimizer would surely go for the nested loop lookup. Looking up just one row is easy, right? But instead it decided to s can the clustered index. (Note: The nonclustered index is functional it will use it if I omit the City column from the query. But it does not seem to want to do a key lookup from it in my testing here. Wacky!) Clustered Index Scan FINDING: INLINE INDEX CREATION IS THE MOST AWESOME FOR TEMP TABLES I like the new syntax for its added flexibility. I do wish it allowed included c olumns, but for indexes with just key columns it can make for tidier, more compa ct code. This feature doesn t fix all the flaws of table variables it s interesting that I did n t get a nested loop lookup in my testing, which makes me wonder if the optimizer has as many options with the indexes on table variables. The ability of temporary tables to have column and index related statistics stil l gives them a great advantage in most situations. Using the inline index creati on script on temporary tables in stored procedure to improve caching is a nice l ittle bonus. Even with the gotcha I linked to above about statistics updates on temp tables, I think this feature makes the case for temporary tables even stron ger. WHAT ABOUT MEMORY OPTIMIZED TABLE VARIABLES? First off, this feature is more expensive. SQL Server 2014 adds in new memory opt imized tables, AKA Project Hekaton . This feature is only available in Enterprise Ed

ition. I won t come close to covering the whole feature here I ll just scratch the su rface of one of its uses: the Memory Optimized Table Variable . The first thing of note is that memory optimized tables DO support statistics so does that mean that a Memory Optimized Table Variable might have them? Let s take a look! FIRST, LET S ENABLE MEMORY OPTIMIZED OBJECTS To test this feature out, I need to make some changes to my test database: ALTER DATABASE [IndexTest] ADD FILEGROUP [ImaNewFileGroup] CONTAINS MEMORY_OPTIM IZED_DATA GO ALTER DATABASE [IndexTest] ADD FILE (name='imanewfilegroup_1', filename='C:\MSSQ L\Data\imanewfilegroup_1') TO FILEGROUP [ImaNewFileGroup]; GO ALTER DATABASE [IndexTest] SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT ON; GO MEMORY_OPTIMIZED = ON ! Now I can start creating my memory optimized table variable. First, I must creat e a table type with the table variable s definition. I m not going to test this in a natively compiled stored procedure just yet, so I m leaving off the identity (it s not supported in this scenario). I also use some special collations and don t allo w nullability on some columns to get the whole thing to work. CREATE TYPE TM1 as TABLE ( City VARCHAR(60) COLLATE Latin1_General_100_BIN2, StateProvinceID INT NOT NULL, INDEX ixTestMe_StateProvinceID NONCLUSTERED (StateProvinceID) ) WITH (MEMORY_OPTIMIZED = ON) All right, now that our table type exists, we can put it to use! Let s populate it and query it and check out what plan we get: DECLARE @TestMe TM1; INSERT @TestMe (City,StateProvinceID) SELECT City, StateProvinceID FROM AdventureWorks2012.Person.[Address]; SELECT City FROM @TestMe WHERE StateProvinceID=1; GO There s a little surprise in the execution plan: Index Seek on Memory Optimized Index Estimated rows is still 1 statistics still aren t working here. But magically we ge t an index seek on the nonclustered index instead of a scan on the clustered ind ex. This is remarkable because back in the normal world of non-memory-optimized indexes, the City column would not be in that nonclustered index we only asked fo r it to contain StateProvinceID! MEMORY OPTIMIZED INDEXES ARE ALWAYS COVERING

S-ar putea să vă placă și