HI folks,A few weeks ago one of my colleagues asked me about a “peculiar” message when tried to use a table variable declared as the user defined table type. The table type had a couple of computed columns. One of the columns had a CASE statement in its definition.The message was:“Metadata stored on disk for computed column '<Columm_name>' in table '<table_variable_name>' did not match the column definition. In order to avoid possible index corruption, please drop and recreate this computed column.”[i]Note[/i]: I found a number of posts on Google about similar messages related to the db migration between servers and possible computed columns metadata mismatch.Since I’ve never come across this message I thought it would be interesting to investigate and try to understand what is that all about.Let’s first try to recreate the warning/message.[i]The test environment params:[/i]• SQL Server version: Microsoft SQL Server 2014 - Developer Edition (64-bit) • Server Instance collation: Latin1_General_BIN[b]Create test database[/b]. The database has different collation than the server instance.[code="sql"]CREATE DATABASE TestUdtTableType COLLATE Latin1_General_CI_ASGOUSE TestUdtTableTypeGO[/code]Now, create a test User defined table type. ([b]script #1[i][/i][/b])[code="sql"]IF EXISTS(SELECT 1 FROM sys.types t WHERE t.name = N'TestUDT') DROP TYPE dbo.TestUDTGO[/code]--Create User Defined Table type[code="sql"]CREATE TYPE dbo.TestUDT AS TABLE ( col1 NVARCHAR(100) ,col2 BIT ,colcal1 AS CASE ISNULL(col2,0) WHEN 1 THEN N'Yes' ELSE 'No' END ,colcal2 AS col1 +' ..this is not unicode suffix'); GO[/code]And finally the message(or warning) shows up after inserting the test values into a table variable declared as the user defined table type dbo.TestUDT ([b]script #2[/b])[code="sql"]DECLARE @tc AS dbo.TestUDTINSERT INTO @tc (col1, col2) SELECT N'Test unicode string',1 UNION ALL SELECT N'and another unicode str. ',0SELECT * FROM @tc[/code]The messages[i]Metadata stored on disk for computed column 'colcal1' in table '@tc' did not match the column definition. In order to avoid possible index corruption, please drop and recreate this computed column.[/i][i]Metadata stored on disk for computed column 'colcal1' in table '@tc' did not match the column definition. In order to avoid possible index corruption, please drop and recreate this computed column.[/i](2 row(s) affected)(2 row(s) affected)[b]NOTES[i][/i][/b]: - If we run [b]script#2[/b] again the message(s) disappears. It may be worthwhile to explore the proc. cache. later :-D - The message suggests dropping and recreating the computed column. ALTER/DROP COLUMN cannot be used with the table variables. - BOL:[i]User defined Table types is the definition of a table structure that can be used to structure sp/function READONLY table parameters and/or to declare table variables within a batch, stored proc. or function[/i]The resultset[img]http://www.sqlservercentral.com/Forums/Attachment17795.aspx[/img]The first metadata message/warning is related to the INSERT statement and the second one to the following SELECT statement[b]The test case analysis:[/b][b]- Available metadata related to the UDT table[/b]Drop and Create the UDT ([b]script #1[/b])The query below shows the metadata related to the UDT and the calculated columns.The metadata related to the user defined table type:[code="sql"]SELECT tab.name AS [user defined table type name] ,TYPE_NAME(tab.system_type_id) AS [System type] ,TYPE_NAME(tab.user_type_id) AS [User type] ,tab.collation_name ,tab.type_table_object_idFROM sys.table_types tabWHERE tab.name ='TestUDT'[/code][img]http://www.sqlservercentral.com/Forums/Attachment17797.aspx[/img]The metadata related to the calculated fields used in the UDT definition ([b]Script #3[/b]):[code="sql"]SELECT OBJECT_NAME(scc.object_id) AS [Internal UDT name] ,scc.column_id AS [Calculated column ordinal position] ,TYPE_NAME(scc.system_type_id) AS [System type name] ,TYPE_NAME(scc.user_type_id) AS [User type name] ,scc.collation_name ,scc.uses_database_collation AS [Collation: 1- database default] ,scc.[definition]FROM sys.computed_columns sccWHERE scc.name IN ('colcal1','colcal2')[/code][img]http://www.sqlservercentral.com/Forums/Attachment17796.aspx[/img]The latest shows:- Both computed columns have the current DB default collation which is[b] Latin1_General_CI_AS[/b]. That was expected since, if not specified, the UDT table columns will use the current DB collation. This is applicable to char, varchar ,text, nchar, nvarchar and ntext column data types- The calculated column [b]colcal1[/b] is of the type [b]NVARCHAR[/b].Calculated column colcal1 type is the result of the CASE statement expression. The column’s data type will be the data type with the highest precedence of all possible result values in the CASE Statement. In this case the CASE statement returns one of the two possible results: ‘Yes’ of type NVARCHAR and ‘No’ of type VARCHAR. The expression uses NVARCHAR because it has higher precedence than VARCHAR [b]- Table variable metadata[/b]The tsql [b]table variable[/b] metadata is stored in the [b]tempdb [/b]database (similar to the temp tables). We need to query the metadata within the same batch since the scope of the tsql variables is from the point it’s declared until the end of the batch (or the hosting stored procedure). --the test ([b]Script #4[/b])[code="sql"]DECLARE @tc AS dbo.TestUDTINSERT INTO @tc (col1, col2) SELECT N'Test unicode string',1 UNION ALL SELECT N'and another unicode str. ',0[/code]--the metadata that shows the columns’ collation[code="sql"]SELECT c.name ,c.collation_nameFROM tempdb.sys.sysobjects so INNER JOIN tempdb.sys.columns c ON so.id = c.object_idWHERE c.name IN('col1','col2','colcal1','colcal2')[/code]The results:[img]http://www.sqlservercentral.com/Forums/Attachment17791.aspx[/img]The metadata stored in tempdb shows that the collation corresponds to the current database default collation. (The [b]instance collation[/b] is [b]Latin1_General_BIN[/b]).The table variable definition([b]Script #4[/b]) seems to match the variable’s template([b]Script #3[/b]) - the user defined table type. [i]The metadata seems to be in order - opposed to what the message says.[/i]As mentioned before, if we run the script again the message disappears. Ok, the column’s metadata wasn’t a good match in the first run, but that changed in the subsequent runsClear the DB cache and try it again.[code="sql"] DECLARE @pInt int = DB_ID('TestUdtTableType') DBCC FLUSHPROCINDB (@pInt) GO[/code]Run the And yep, the message popped up again.Just because it’s fun I made a detour to find out the table’s variable cached data – plan cache.--clean the database query cache and run the exact same query 5 times.[code="sql"]DECLARE @tc AS dbo.TestUDTINSERT INTO @tc (col1, col2) SELECT N'Test unicode string',1 UNION ALL SELECT N'and another unicode str. ',0--SELECT * FROM @tcGO 5[/code]--check the Adhoc cache[code="sql"]SELECT cp.bucketid ,cp.refcounts ,cp.usecounts ,cp.size_in_bytes ,cp.cacheobjtype ,cp.objtype ,txt.[text] ,cp.plan_handle --,cp.parent_plan_handleFROM sys.dm_exec_cached_plans cp CROSS APPLY sys.dm_exec_sql_text (plan_handle) txtWHERE objtype ='Adhoc' AND txt.text not like '%sys.dm_exec_cached_plans%' AND txt.dbid = DB_ID('TestUdtTableType')[/code]As expected, the plan has been cached as Adhoc compiled plan. (The option Optimize for ad hoc workloads is set to 0).[b]NOTE:[/b] If Optimize for ad hoc workloads is set to 1 the two set of messages will show up. The first related to the compiled query stub, and the second one will be related to the actual compiled plan. The compiled plan will be used 4 times outputting one message.The message popped up only after the first batch and then, when sql server started to use the cached plans for the next 4 batches the message disappeared.[img]http://www.sqlservercentral.com/Forums/Attachment17792.aspx[/img]My assumption is that the metadata related to the user defined table type are compared in terms of the collation with the table value metadata during the construction of the plan cache. For some “[b]reason[/b]” the message pops up even if the collations are identical and cannot affect index corruption. :-)There is another interesting thing I found when forced the computed column with the CASE statement to return [b]sql_variant[/b] datatype.[i]NOTE[/i]: sql_variant data type has the highest datatype president and will be used as the expression datatype[code="sql"]IF EXISTS(SELECT 1 FROM sys.types t WHERE t.name = N'TestUDT') DROP TYPE dbo.TestUDT[/code]--Create User Defined Table type[code="sql"]CREATE TYPE dbo.TestUDT AS TABLE ( col1 NVARCHAR(100) ,col2 BIT ,colcal1 AS CASE ISNULL(col2,0) WHEN 1 THEN N'Yes' WHEN 0 then 'No' ELSE CAST(NULL AS sql_variant) END ,colcal2 AS col1 +' ..this is not an Unicode suffix'); GO[/code]The computed columns metadata ([b]script#3[/b]) will return[img]http://www.sqlservercentral.com/Forums/Attachment17793.aspx[/img].. and because the underlying base type of the sql_variant expression result is evaluated at run time the collation_name is NULLThe interesting thing is the “uses_database_collation” column that has the value of 1 – the default DB collation, but when we use the data type the collation will be the instance collation.Now, when we run the query similar to ([b]Script #4[/b])[code="sql"]DECLARE @tc AS dbo.TestUDTINSERT INTO @tc (col1, col2) SELECT N'Test unicode string',1 UNION ALL SELECT N'and another unicode str. ',0SELECT * ,SQL_VARIANT_PROPERTY(colcal1,'Collation') [Base Type Collation] ,SQL_VARIANT_PROPERTY(colcal1,'BaseType') [Base Type] FROM @tc[/code][img]http://www.sqlservercentral.com/Forums/Attachment17794.aspx[/img]The message did not show up and the base type collation is the instance default rather than the database default as expected.[b]And finally[/b], if we explicitly specify collation in the CASE statement, the message will not show up[code="sql"]IF EXISTS(SELECT 1 FROM sys.types t WHERE t.name = N'TestUDT') DROP TYPE dbo.TestUDT--Create User Defined Table typeCREATE TYPE dbo.TestUDT AS TABLE ( col1 NVARCHAR(100) ,col2 BIT ,colcal1 AS CASE ISNULL(col2,0) WHEN 1 THEN N'Yes' ELSE 'No' COLLATE Latin1_General_CI_AS END ,colcal2 AS col1 +' ..this is not an Unicode suffix'); GO[/code]If anyone have an idea why the message is showing and how to explain the presented behavior, please comment.
↧