sql-server – 兼容级别80的实际行为是什么?
有人可以让我更好地了解兼容模式功能吗?它的行为与我的预期不同.
据我了解兼容模式,它是关于各种版本的SQL Server之间的某些语言结构的可用性和支持. 它不会影响数据库引擎版本的内部工作方式.它会尝试阻止使用早期版本中尚未提供的功能和构造. 我刚刚在SQL Server 2008 R2中创建了一个compat等级为80的新数据库.创建一个包含单个int列的表,并用几行填充它. 然后执行带有row_number()函数的select语句. 我的想法是,因为row_number函数仅在2005年引入,这将在compat 80模式中引发错误. 但令我惊讶的是,这很好.然后,当然,只有在你“保存”之后才会评估compat规则.所以我为row_number语句创建了一个存储过程. 存储过程创建很顺利,我可以完美地执行它并获得结果. 有人可以帮助我更好地理解兼容模式的工作吗?我的理解显然是有缺陷的. 解决方法从 the docs开始:
在我的解释中,兼容模式是关于语法的行为和解析,而不是像解析器说的那样,“嘿,你不能使用ROW_NUMBER()!”有时,较低的兼容性级别允许您继续使用不再支持的语法,有时它会阻止您使用新的语法结构. The documentation列出了几个明确的例子,但这里有一些演示: 将内置函数作为函数参数传递 此代码适用于兼容级别90: SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(),NULL,NULL); 但在80年代它产生:
这里的具体问题是在80年代,你不允许将内置函数传递给函数.如果您想保持80兼容模式,可以通过以下方式解决此问题: DECLARE @db_id INT = DB_ID(); SELECT * FROM sys.dm_db_index_physical_stats(@db_id,NULL); 将表类型传递给表值函数 与上面类似,使用TVP并尝试将其传递给表值函数时,可能会出现语法错误.这适用于现代compat级别: CREATE TYPE dbo.foo AS TABLE(bar INT); GO CREATE FUNCTION dbo.whatever ( @foo dbo.foo READONLY ) RETURNS TABLE AS RETURN (SELECT bar FROM @foo); GO DECLARE @foo dbo.foo; INSERT @foo(bar) SELECT 1; SELECT * FROM dbo.whatever(@foo); 但是,将兼容级别更改为80,然后再次运行最后三行;您收到此错误消息:
除了升级compat级别或以不同的方式获得结果之外,没有任何好的解决方法. 在APPLY中使用限定列名称 在90兼容模式及以上,您可以毫无问题地执行此操作: SELECT * FROM sys.dm_exec_cached_plans AS p CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t; 但是,在80兼容模式下,传递给函数的限定列会引发一般语法错误:
ORDER BY恰好与列名匹配的别名 考虑这个查询: SELECT name = REVERSE(name),realname = name FROM sys.all_objects AS o ORDER BY o.name; 在80兼容模式下,结果如下: 001_ofni_epytatad_ps sp_datatype_info_100 001_scitsitats_ps sp_statistics_100 001_snmuloc_corps_ps sp_sproc_columns_100 ... 在90兼容模式下,结果完全不同: snmuloc_lla all_columns stcejbo_lla all_objects sretemarap_lla all_parameters ... 原因?在80兼容模式下,表前缀被完全忽略,因此它按SELECT列表中别名定义的表达式排序.在较新的兼容级别中,会考虑表前缀,因此SQL Server实际上会在表中使用该列(如果找到).如果在表中找不到ORDER BY别名,则较新的兼容性级别对于歧义不那么宽容.考虑这个例子: SELECT myname = REVERSE(name),realname = name FROM sys.all_objects AS o ORDER BY o.myname; 结果按80中的myname表达式排序,因为再次忽略表前缀,但在90中它会生成以下错误消息:
在the documentation中也解释了这一点:
ORDER BY不在SELECT列表中的东西 在90兼容模式下,您无法执行此操作: SELECT name = COALESCE(a.name,'') FROM sys.objects AS a UNION ALL SELECT name = COALESCE(a.name,'') FROM sys.objects AS a ORDER BY a.name; 结果:
但是,在80年代,您仍然可以使用此语法. 旧的,icky外连接 80模式还允许您使用旧的,不推荐使用的外连接语法(* = / = *): SELECT o.name,c.name FROM sys.objects AS o,sys.columns AS c WHERE o.[object_id] *= c.[object_id]; 在SQL Server 2008/2008 R2中,如果您处于90或更高版本,则会收到以下详细消息:
在SQL Server 2012中,这根本不再是有效的语法,并产生以下结果:
当然,在SQL Server 2012中,您无法再使用兼容级别来解决此问题,因为不再支持80.如果以80 compat模式升级数据库(通过就地升级,分离/附加,备份/恢复,日志传送,镜像等),它将自动升级到90. 没有WITH的表提示 在80 compat模式下,您可以使用以下内容并观察表提示: SELECT * FROM dbo.whatever NOLOCK; 在90年代,NOLOCK不再是表格提示,它是别名.否则,这将工作: SELECT * FROM dbo.whatever AS w NOLOCK; 但它没有:
现在,为了证明在90 compat模式下第一个示例中没有观察到该行为,请使用AdventureWorks(确保它处于更高的compat级别)并运行以下命令: BEGIN TRANSACTION; SELECT TOP (1) * FROM Sales.SalesOrderHeader UPDLOCK; SELECT * FROM sys.dm_tran_locks WHERE request_session_id = @@SPID AND resource_type IN ('KEY','OBJECT'); -- how many rows here? 0 COMMIT TRANSACTION; BEGIN TRANSACTION; SELECT TOP (1) * FROM Sales.SalesOrderHeader WITH (UPDLOCK); SELECT * FROM sys.dm_tran_locks WHERE request_session_id = @@SPID AND resource_type IN ('KEY','OBJECT'); -- how many rows here? 2 COMMIT TRANSACTION; 这个问题尤其成问题,因为行为在没有错误消息甚至错误的情况下发生变化.它也是升级顾问和其他工具甚至可能都没有发现的东西,因为众所周知,这是一个表别名. 涉及新日期/时间类型的转化 SQL Server 2008中引入的新日期/时间类型(例如date和datetime2)支持的范围比原始datetime和smalldatetime大得多.无论兼容级别如何,显式转换支持范围之外的值都将失败,例如: SELECT CONVERT(SMALLDATETIME,'00010101'); 产量:
但是,隐式转换将在较新的兼容级别中发挥作用.例如,这将在100中工作: SELECT DATEDIFF(DAY,CONVERT(SMALLDATETIME,SYSDATETIME()),'00010101'); 但是在80(以及90)中,它会产生与上面类似的错误:
触发器中的冗余FOR子句 这是an obscure scenario that came up here.在80兼容模式下,这将成功: CREATE TABLE dbo.x(y INT); GO CREATE TRIGGER tx ON dbo.x FOR UPDATE,UPDATE ------------^^^^^^ notice the redundant UPDATE AS PRINT 1; 在90兼容性和更高,这不再解析,而是您收到以下错误消息:
PIVOT / UNPIVOT 某些形式的语法在80下不起作用(但在90中工作得很好): SELECT col1,col2 FROM dbo.t1 UNPIVOT (value FOR col3 IN ([x],[y])) AS p; 这会产生:
对于某些变通方法,包括CROSS APPLY,请参阅these answers. 新的内置功能 尝试在兼容级别<的数据库中使用TRY_CONVERT()等新函数. 110.根本就没有人认识到他们. SELECT TRY_CONVERT(INT,1); 结果:
建议 如果您确实需要,只能使用80兼容模式.由于它在2008 R2之后的下一个版本中将不再可用,您要做的最后一件事就是在此compat级别编写代码,依赖于您看到的行为,然后在您不能再使用时会出现一大堆破损使用那个compat级别.要有前瞻性思维,不要试图通过花时间继续使用旧的,不推荐使用的语法来将自己描绘成一个角落. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |