호환성 수준 80의 실제 동작은 무엇입니까?


47

누군가가 호환 모드 기능에 대한 더 나은 통찰력을 제공 할 수 있습니까? 예상과 다른 동작입니다.

호환성 모드를 이해하는 한, 다양한 버전의 SQL Server간에 특정 언어 구조의 가용성 및 지원에 관한 것입니다.

데이터베이스 엔진 버전의 내부 작업에는 영향을 미치지 않습니다. 이전 버전에서는 아직 사용할 수 없었던 기능 및 구성을 사용하지 않으려 고 시도했습니다.

SQL Server 2008 R2에서 compat 레벨 80으로 새 데이터베이스를 만들었습니다. 하나의 int 열이있는 테이블을 만들고 몇 개의 행으로 채 웁니다.

그런 다음 row_number()함수를 사용 하여 select 문을 실행했습니다 .

row_number 함수는 2005 년에 도입되었으므로 compat 80 모드에서 오류가 발생합니다.

그러나 놀랍게도 이것은 잘 작동했습니다. 그런 다음 반드시 '무엇을 저장'한 후에 만 ​​compat 규칙을 평가해야합니다. 그래서 row_number 문에 대해 저장된 proc을 만들었습니다.

저장된 proc 생성이 잘되어 완벽하게 실행하고 결과를 얻을 수 있습니다.

호환 모드의 작동을 더 잘 이해하는 데 도움이 될 수 있습니까? 나의 이해에는 분명히 결함이있다.

답변:


66

에서 워드 프로세서 :

특정 데이터베이스 동작이 지정된 버전의 SQL Server와 호환되도록 설정합니다.
...
호환성 수준은 이전 버전의 SQL Server 만 부분 이전 버전과의 호환성을 제공합니다. 호환성 수준을 임시 마이그레이션 보조 도구로 사용하여 관련 호환성 수준 설정으로 제어되는 동작의 버전 차이를 해결하십시오.

내 해석에서 호환성 모드는 구문 분석기가 "이봐, 당신은 사용할 수 없습니다 ROW_NUMBER()!" 라고 말하는 것이 아니라 구문의 동작과 구문 분석에 관한 것 입니다. 때때로 호환성 수준이 낮아지면 더 이상 지원되지 않는 구문을 계속 사용할 수 있으며 때로는 새로운 구문 구성을 사용할 수 없습니다. 이 문서 에는 몇 가지 명시적인 예가 나와 있지만 다음은 몇 가지 데모입니다.


내장 함수를 함수 인수로 전달

이 코드는 호환성 수준 90 이상에서 작동합니다.

SELECT *
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL);

그러나 80에서는 다음과 같은 결과를 얻습니다.

메시지 102, 수준 15, 상태 1
'('근처의 구문이 잘못되었습니다.

여기서 특정 문제는 80에서 내장 함수를 함수에 전달할 수 없다는 것입니다. 80 호환성 모드를 유지하려면 다음과 같이 말하면이 문제를 해결할 수 있습니다.

DECLARE @db_id INT = DB_ID();

SELECT * 
FROM sys.dm_db_index_physical_stats(@db_id, NULL, NULL, NULL, 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으로 변경하고 마지막 세 줄을 다시 실행하십시오. 이 오류 메시지가 나타납니다.

메시지 137, 수준 16, 상태 1, 줄 19
스칼라 변수 "@foo"를 선언해야합니다.

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 호환성 모드에서 함수에 전달 된 규정 된 열은 일반 구문 오류를 발생시킵니다.

메시지 102, 수준 15, 상태 1
'.'근처의 구문이 잘못되었습니다.


열 이름과 일치하는 별명으로 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;

결과는 myname테이블 접두어가 다시 무시되기 때문에 80 의 표현식 순서로 정렬 되지만 90에서는 다음 오류 메시지가 생성됩니다.

메시지 207, 수준 16, 상태 1, 줄 3
잘못된 열 이름 'myname'입니다.

이것은 모두 문서 에 설명되어 있습니다 .

ORDER BY목록 의 열 참조를 목록에 정의 된 열에 바인딩하면 열 SELECT모호성이 무시되고 열 접두사가 무시되는 경우가 있습니다. 결과 집합이 예기치 않은 순서로 반환 될 수 있습니다.

예를 들어, SELECT 목록의 열에 대한 참조로 사용되는 ORDER BY단일 두 부분 열 ( <table_alias>.<column>)이 있는 절 은 허용되지만 테이블 별명은 무시됩니다. 다음 쿼리를 고려하십시오.

SELECT c1 = -c1 FROM t_table AS x ORDER BY x.c1

실행될 때 열 접두사는 무시됩니다 ORDER BY. 지정된 소스 열 ( x.c1) 에서 정렬 작업이 예상대로 수행되지 않습니다 . 대신 파생에서 발생합니다c1쿼리에 정의 된 열입니다. 이 쿼리의 실행 계획은 파생 열의 값이 먼저 계산 된 다음 계산 된 값이 정렬됨을 보여줍니다.


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;

결과:


문에 UNION, INTERSECT 또는 EXCEPT 연산자가 포함 된 경우 메시지 104, 수준 16, 상태 1 ORDER BY 항목이 선택 목록에 나타나야합니다.

그러나 80에서이 구문을 계속 사용할 수 있습니다.


오래되고 이기적인 외부 조인

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 이상인 경우 다음과 같은 자세한 메시지가 표시됩니다.

메시지 4147, 수준 15, 상태 1
쿼리는 ANSI 이외의 외부 조인 연산자 ( " *="또는 " =*")를 사용합니다. 이 쿼리를 수정하지 않고 실행하려면 ALTER DATABASE의 SET COMPATIBILITY_LEVEL 옵션을 사용하여 현재 데이터베이스의 호환성 수준을 80으로 설정하십시오. ANSI 외부 조인 연산자 (LEFT OUTER JOIN, RIGHT OUTER JOIN)를 사용하여 쿼리를 다시 작성하는 것이 좋습니다. 이후 버전의 SQL Server에서는 비 ANSI 조인 연산자는 이전 버전과의 호환성 모드에서도 지원되지 않습니다.

SQL Server 2012에서는 이것이 더 이상 유효한 구문이 아니며 다음을 생성합니다.

메시지 102, 수준 15, 상태 1, 줄 3
'* ='근처의 구문이 잘못되었습니다.

물론 SQL Server 2012에서는 호환성 수준을 사용하여이 문제를 더 이상 해결할 수 없습니다. 80은 더 이상 지원되지 않기 때문입니다. 현재 위치 업그레이드, 분리 / 연결, 백업 / 복원, 로그 전달, 미러링 등 80 개 호환 모드에서 데이터베이스를 업그레이드하면 자동으로 90으로 업그레이드됩니다.


WITH없이 테이블 힌트

80 compat 모드에서는 다음을 사용할 수 있으며 테이블 힌트가 나타납니다.

SELECT * FROM dbo.whatever NOLOCK; 

90 NOLOCK이상에서는 더 이상 표 힌트가 아니며 별칭입니다. 그렇지 않으면 다음과 같이 작동합니다.

SELECT * FROM dbo.whatever AS w NOLOCK;

그러나 그렇지 않습니다 :

메시지 1018, 수준 15, 상태 1
'NOLOCK'근처의 구문이 잘못되었습니다. 이것이 테이블 힌트의 일부로 의도 된 경우 이제 WITH 키워드와 괄호가 필요합니다. 적절한 구문은 SQL Server 온라인 설명서를 참조하십시오.

이제 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에 도입 된 새로운 날짜 / 시간 유형 (예 : datedatetime2)은 원본 datetimesmalldatetime) 보다 훨씬 큰 범위를 지원합니다 . 지원되는 범위를 벗어난 값의 명시 적 변환은 호환성 수준에 관계없이 실패합니다 (예 :

SELECT CONVERT(SMALLDATETIME, '00010101');

수율 :

메시지 242, 수준 16, 상태 3
varchar 데이터 형식을 smalldatetime 데이터 형식으로 변환하면 범위를 벗어난 값이 발생했습니다.

그러나 암시 적 변환은 새로운 호환성 수준에서 스스로 작동합니다. 예를 들어 100 이상에서 작동합니다.

SELECT DATEDIFF(DAY, CONVERT(SMALLDATETIME, SYSDATETIME()), '00010101');

그러나 80 (및 90)에서도 위와 유사한 오류가 발생합니다.

메시지 242, 수준 16, 상태 3
varchar 데이터 형식을 날짜 / 시간 데이터 형식으로 변환하면 범위를 벗어난 값이 발생했습니다.


트리거의 중복 FOR 절

이것은 여기에 나온 모호한 시나리오입니다 . 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 이상이면 더 이상 구문 분석되지 않고 대신 다음 오류 메시지가 표시됩니다.

메시지 1034, 수준 15, 상태 1, 절차 tx
구문 오류 : 트리거 선언에서 "UPDATE"작업의 사양이 중복되었습니다.


피봇 / UNPIVOT

일부 구문 형식은 80 미만에서는 작동하지 않지만 90 이상에서는 정상적으로 작동합니다.

SELECT col1, col2
FROM dbo.t1
UNPIVOT (value FOR col3 IN ([x],[y])) AS p;

결과는 다음과 같습니다.

메시지 156, 수준 15, 상태 1
키워드 'for'근처의 구문이 잘못되었습니다.

를 포함한 일부 해결 방법 CROSS APPLY다음 답변을 참조하십시오 .


새로운 내장 기능

TRY_CONVERT()호환성 수준 <110 인 데이터베이스와 같은 새로운 기능을 사용해보십시오 . 전혀 인식되지 않습니다.

SELECT TRY_CONVERT(INT, 1);

결과:

메시지 195, 수준 15, 상태 10
'TRY_CONVERT'은 (는) 인식 된 기본 제공 함수 이름이 아닙니다.


추천

실제로 필요한 경우에만 80 호환 모드를 사용 하십시오. 2008 R2 이후의 다음 버전에서는 더 이상 사용할 수 없으므로 마지막으로 수행하려는 작업은이 호환성 수준에서 코드를 작성하고 사용자가 보는 동작에 의존 한 다음 더 이상 할 수 없을 때 완전히 파손 된 것입니다. 그 동료 수준을 사용하십시오. 앞으로 생각하고 오래되고 더 이상 사용되지 않는 구문을 계속 사용하기 위해 시간을 벌어 구석에 자신을 페인트하려고하지 마십시오.


1
분명히 그것은 내 것보다 더 나은 대답입니다!
맥스 버논

이 정교한 답변에 감사드립니다. Aaron! 그리고 나의 수많은 철자 실수를 고치기 위해.
souplex

1
SQL 서버 2014 호환성 노트는 여기에서 찾을 수 있습니다 msdn.microsoft.com/en-us/library/bb510680(v=sql.120).aspx
조쉬 갤러거를

9

호환성 수준은 이전 버전의 SQL Server에서 제어 된 마이그레이션을 허용하기 위해서만 제공됩니다. Compat Level 90은 새로운 기능의 사용을 배제하지 않으며 단순히 데이터베이스의 특정 측면이 SQL Server 2005의 작동 방식과 호환되는 방식으로 유지됨을 의미합니다.

자세한 내용은 http://msdn.microsoft.com/en-us/library/bb510680.aspx 를 참조 하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.