Uyumluluk düzeyi 80'in gerçek davranışı nedir?


47

Biri bana uyumluluk modu özelliği hakkında daha iyi bir fikir verebilir mi? Beklediğimden farklı davranıyor.

Uyumluluk modlarını anladığım kadarıyla, SQL Server'ın çeşitli sürümleri arasındaki belirli dil yapılarının kullanılabilirliği ve desteği ile ilgili.

Veritabanı motoru sürümünün iç işleyişini etkilemez. Daha önceki sürümlerde henüz bulunmayan özelliklerin ve yapıların kullanılmasını önlemeye çalışırdı.

SQL Server 2008 R2’de 80 uyumlu bir seviye ile yeni bir veritabanı oluşturdum. Tek bir int sütunu içeren bir tablo oluşturuldu ve birkaç satırla doldurdu.

Sonra bir row_number()fonksiyon ile bir select ifadesi yürüttü.

Düşünceme göre, row_number işlevi yalnızca 2005'te tanıtıldığından bu, 80 uyumlu modda bir hataya neden olacaktı.

Ama benim için sürpriz bu işe yaradı. Daha sonra, elbette, uyum kuralları yalnızca bir şeyi 'kaydettiğinizde' değerlendirilir. Böylece row_number ifadem için saklı bir proc oluşturdum.

Depolanan proc oluşturma işlemi iyi gitti ve ben bunu mükemmel şekilde uygulayıp sonuçları alabilirim.

Biri uyumluluk modunun çalışmasını daha iyi anlamama yardımcı olabilir mi? Benim anlayışım açıkça kusurlu.

Yanıtlar:


66

Gönderen docs :

Bazı veritabanı davranışlarını, belirtilen SQL Server sürümüyle uyumlu olacak şekilde ayarlar.
...
Uyumluluk seviyesi, SQL Server'ın önceki sürümleriyle yalnızca kısmen geriye dönük uyumluluk sağlar. İlgili uyumluluk düzeyi ayarıyla kontrol edilen davranışlardaki sürüm farklılıkları etrafında çalışmak için uyumluluk düzeyini geçici bir geçiş yardımcısı olarak kullanın.

Benim yorumumda, uyumluluk modu, "Ayrıştırıcı kullanamazsınız ROW_NUMBER()!" Diyen şeyler için değil, sözdizimi davranış ve ayrıştırma hakkındadır . Bazen düşük uyumluluk seviyesi, artık desteklenmeyen sözdizimi ile kurtulmaya devam etmenize izin verir ve bazen yeni sözdizimi yapıları kullanmanızı önler. Belgeler birkaç açık örneği listeliyor, ancak işte birkaç örnek:


Yerleşik işlevleri işlev argümanları olarak geçirme

Bu kod 90+ uyumluluk düzeyinde çalışır:

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

Ancak 80 yılında verim:

Mesaj 102, Seviye 15, Durum 1
'(' yakınında yanlış sözdizimi.

Buradaki spesifik sorun, 80'de yerleşik bir işlevi bir işleve geçirmenize izin verilmemesidir. 80 uyumluluk modunda kalmak istiyorsanız, şunu söyleyerek bu sorunu çözebilirsiniz:

DECLARE @db_id INT = DB_ID();

SELECT * 
FROM sys.dm_db_index_physical_stats(@db_id, NULL, NULL, NULL, NULL);

Tablo türündeki bir tablo türüne bir tablo türü iletme

Yukarıdakilere benzer şekilde, TVP kullanırken ve tablo değerli bir işleve aktarmaya çalışırken bir sözdizimi hatası alabilirsiniz. Bu, modern uyumluluk seviyelerinde çalışır:

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);

Ancak, uyumluluk düzeyini 80 olarak değiştirin ve son üç satırı tekrar çalıştırın; bu hata iletisini alıyorsunuz:

137, Seviye 16, Durum 1, Satır 19
Skaler değişkeni "@foo" olarak bildirmelidir.

Uyumluluk seviyesini yükseltmek veya sonuçları farklı bir yoldan almak dışında, başımın üstünden gerçekten iyi bir çözüm gelmiyor.


APPLY'de nitelikli sütun adlarını kullanma

90 uyumluluk modunda ve daha fazlasını yaparken, bunu sorunsuzca yapabilirsiniz:

SELECT * FROM sys.dm_exec_cached_plans AS p
  CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t;

Bununla birlikte, 80 uyumluluk modunda, işleve verilen nitelikli sütun genel bir sözdizimi hatası oluşturur:

Mesaj 102, Seviye 15, Durum 1
'.' Yakınında yanlış sözdizimi.


Sütun adıyla eşleşen bir diğer adı SİPARİŞ VER

Bu sorguyu düşünün:

SELECT name = REVERSE(name), realname = name 
FROM sys.all_objects AS o
ORDER BY o.name;

80 uyumluluk modunda, sonuçlar aşağıdaki gibidir:

001_ofni_epytatad_ps   sp_datatype_info_100
001_scitsitats_ps      sp_statistics_100
001_snmuloc_corps_ps   sp_sproc_columns_100
...

90 uyumluluk modunda, sonuçlar oldukça farklıdır:

snmuloc_lla      all_columns
stcejbo_lla      all_objects
sretemarap_lla   all_parameters
...

Sebep? 80 uyumluluk modunda, tablo öneki tamamen göz ardı edilir, bu nedenle SELECTlistede takma ad tarafından tanımlanan ifadeye göre sıralanır . Daha yeni uyumluluk seviyelerinde, tablo öneki göz önünde bulundurulur, bu nedenle SQL Server gerçekte tablodaki bu sütunu kullanır (eğer bulunursa). Eğer ORDER BYtakma tabloda bulunamazsa, yeni uyumluluğu düzeyleri çok belirsizlik hakkında bağışlayıcı değildir. Bu örneği düşünün:

SELECT myname = REVERSE(name), realname = name 
FROM sys.all_objects AS o
ORDER BY o.myname;

Sonuç myname80'deki ifade ile sıralanır , çünkü yine tablo öneki yoksayılır, ancak 90'da bu hata iletisini oluşturur:

Mesaj 207, Seviye 16, Durum 1, Satır 3
Geçersiz sütun adı 'myname'.

Bunların hepsi belgelerde de açıklanmıştır :

ORDER BYListedeki sütun referanslarını listede tanımlanan sütunlara bağlarken SELECT, sütun belirsizlikleri yoksayılır ve sütun önekleri bazen yoksayılır. Bu, sonuç kümesinin beklenmedik bir sırada dönmesine neden olabilir.

Örneğin, bir SELECT listesindeki bir sütuna referans olarak kullanılan, ORDER BYtek bir iki parçalı sütuna ( <table_alias>.<column>) sahip bir cümle kabul edilir, ancak takma ad yoksayılır. Aşağıdaki sorguyu düşünün.

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

Yürütüldüğünde, sütun öneki ORDER BY. Sıralama işlemi, x.c1beklendiği gibi belirtilen kaynak sütunda ( ) gerçekleşmez; bunun yerine türetilmişc1sorguda tanımlanan sütun. Bu sorgu için yürütme planı, türetilmiş sütun için değerlerin önce hesaplandığını ve sonra hesaplanan değerlerin sıralandığını gösterir.


SELECT listesinde olmayan bir şeyi SİPARİŞ VER

90 uyumluluk modunda bunu yapamazsınız:

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;

Sonuç:

Mesaj 104, Seviye 16, Durum 1
SİPARİŞ, KESİNLİK veya HARİCİ bir işleç içeriyorsa, seçim listesinde SİPARİŞ BY öğeleri görünmelidir.

80 yılında, yine de, bu sözdizimini kullanabilirsiniz.


Eski, sert dış bağlantılar

80 modu ayrıca eski, kullanımdan kaldırılmış dış birleşim sözdizimini ( *=/=*) kullanmanıza izin verir :

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’de, 90 yaşından büyükseniz, bu ayrıntılı mesajı alırsınız:

Msj 4147, Seviye 15, Durum 1
Sorgu, ANSI dışı harici birleştirme işleçlerini (" *=" veya " =*") kullanır. Bu sorguyu değiştirmeden çalıştırmak için, lütfen ALTER DATABASE SET COMPATIBILITY_LEVEL seçeneğini kullanarak mevcut veritabanının uyumluluk düzeyini 80 olarak ayarlayın. Sorgu ANSI dış birleştirme işleçlerini (LEFT OUTER JOIN, RIGHT OUTER JOIN) kullanarak yeniden yazmanız önemle önerilir. SQL Server'ın gelecekteki sürümlerinde, ANSI olmayan birleştirme işleçleri geriye dönük uyumluluk modlarında bile desteklenmeyecek.

SQL Server 2012'de, bu artık geçerli bir sözdizimi değildir ve aşağıdakileri sağlar:

Mesaj 102, Seviye 15, Durum 1, Satır 3
'* =' yakınında yanlış sözdizimi.

Tabii ki SQL Server 2012'de, artık 80 desteklenmediğinden uyumluluk seviyesini kullanarak bu soruna geçici bir çözüm bulamıyorsunuz. Bir veritabanını 80 uyumlu modda (yerinde yükseltme, ayırma / takma, yedekleme / geri yükleme, günlük gönderme, yansıtma vb.) Yükseltirseniz, sizin için otomatik olarak 90'a yükseltilir.


İLE olmadan Tablo ipuçları

80 uyumlu modda, aşağıdakileri kullanabilirsiniz; tablo ipucu gözlenecektir:

SELECT * FROM dbo.whatever NOLOCK; 

90 + 'da, bu NOLOCKartık bir masa ipucu değil, bir takma addır. Aksi takdirde, bu işe yarar:

SELECT * FROM dbo.whatever AS w NOLOCK;

Ama öyle değil:

1018, Seviye 15, Durum 1
'NOLOCK' yakınında yanlış sözdizimi. Bu tablo ipucunun bir parçası olarak tasarlandıysa, A WITH anahtar sözcüğü ve parantez gerekir. Doğru sözdizimi için SQL Server Books Online'da bakın.

Şimdi, davranışın ilk örnekte 90 uyumluluk modunda gözlenmediğini kanıtlamak için, AdventureWorks'ü kullanın (daha yüksek bir uyumluluk düzeyinde olduğundan emin olun) ve şunları çalıştırın:

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;

Bu özellikle problemlidir çünkü davranış bir hata mesajı veya hatta bir hata olmadan değişir. Ayrıca yükseltme danışmanı ve diğer araçların bile fark edemeyeceği bir şey, çünkü tüm bildiği için bu bir masa takma adı.


Yeni tarih / saat türlerini içeren dönüşümler

SQL Server 2008'de tanıtılan yeni tarih / saat türleri (örn. dateVe datetime2) orijinalden daha büyük bir aralığı desteklemektedir datetimeve smalldatetime). Desteklenen aralık dışındaki değerlerin açıkça dönüştürülmesi, uyumluluk düzeyi ne olursa olsun başarısız olur, örneğin:

SELECT CONVERT(SMALLDATETIME, '00010101');

Verim:

Mesaj 242, Seviye 16, Durum 3
Varchar veri tipinin smalldatetime veri tipine dönüştürülmesi, aralık dışı bir değere yol açtı.

Ancak, örtük dönüşümler daha yeni uyumluluk düzeylerinde işe yarayacak. Örneğin bu 100+ çalışacak:

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

Fakat 80'de (ve 90'da), yukarıdakiyle benzer bir hata veriyor:

Mesaj 242, Seviye 16, Durum 3
Bir varchar veri tipinin bir tarih-saat veri tipine dönüştürülmesi, aralık dışı bir değere yol açtı.


Tetikleyicilerdeki gereksiz FOR cümleleri

Bu, buraya gelen karanlık bir senaryo . 80 uyumluluk modunda bu başarılı olacaktır:

CREATE TABLE dbo.x(y INT);
GO
CREATE TRIGGER tx ON dbo.x
FOR UPDATE, UPDATE
------------^^^^^^ notice the redundant UPDATE
AS PRINT 1;

90 uyumluluk ve daha yüksek sürümlerde, bu artık ayrışmaz ve bunun yerine aşağıdaki hata iletisini alırsınız:

Mesaj 1034, Seviye 15, Durum 1, Prosedür tx
Sözdizimi hatası: Tetikleyici bildiriminde "GÜNCELLEME" eyleminin yinelenen belirtimi.


MİL / UNPIVOT

Bazı sözdizimi formları 80 yaşın altında çalışmaz (ancak 90+ da iyi çalışır):

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

Bu verim:

Mesaj 156, Seviye 15, Durum 1
'for' anahtar sözcüğünün yanında yanlış sözdizimi.

Dahil olmak üzere bazı geçici çözümler CROSS APPLYiçin bu cevaplara bakın .


Yeni yerleşik fonksiyonlar

TRY_CONVERT()Uyumluluk düzeyi <110 olan bir veritabanında olduğu gibi yeni işlevler kullanmayı deneyin . Orada hiç tanınmazlar.

SELECT TRY_CONVERT(INT, 1);

Sonuç:

Mesaj 195, Seviye 15, Durum 10
'TRY_CONVERT' tanınmış bir yerleşik işlev adı değil.


Öneri

Sadece gerçekten ihtiyacınız varsa 80 uyumluluk modunu kullanın . 2008 R2’den sonraki sürümde artık kullanılamayacağından, yapmak istediğiniz son şey, bu uyumluluk düzeyinde kod yazmak, gördüğünüz davranışlara güvenmek ve artık yapamadığınızda bir sürü kırılmaya maruz kalmaktır. bu uyumluluk seviyesini kullanın. İleriye dönük düşünün ve eski, kullanımdan kaldırılmış sözdizimini kullanmaya devam etmek için zaman satın alarak kendinizi bir köşeye boyamaya çalışmayın.


1
Açıkçası bu benimkinden daha iyi bir cevap!
Max Vernon,

Bu ayrıntılı cevap için çok teşekkür ederim Aaron! Ve sayısız yazım hatalarımı düzelttiğim için.
souplex

1
SQL Server 2014 uyumluluk notları burada bulunur: msdn.microsoft.com/en-us/library/bb510680(v=sql.120).aspx
Josh Gallagher

9

Uyumluluk Seviyeleri, yalnızca daha eski bir SQL Server sürümünden kontrollü bir göçe izin vermek için mevcuttur. Uyumluluk Seviyesi 90 yeni özelliklerin kullanılmasını engellemez, sadece veritabanının belirli yönlerinin SQL Server 2005'in nasıl çalıştığıyla uyumlu bir şekilde tutulduğu anlamına gelir.

Daha fazla bilgi için http://msdn.microsoft.com/en-us/library/bb510680.aspx adresine bakın .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.