Apa perilaku aktual tingkat kompatibilitas 80?

47

Bisakah seseorang memberi saya wawasan yang lebih baik tentang fitur mode kompatibilitas? Itu berperilaku berbeda dari yang saya harapkan.

Sejauh yang saya mengerti mode kompatibilitas, ini adalah tentang ketersediaan dan dukungan struktur bahasa tertentu antara berbagai versi SQL Server.

Itu tidak mempengaruhi cara kerja versi mesin database. Itu akan mencoba untuk mencegah penggunaan fitur dan konstruksi yang belum tersedia di versi sebelumnya.

Saya baru saja membuat database baru dengan compat level 80 di SQL Server 2008 R2. Membuat tabel dengan satu kolom int dan mengisinya dengan beberapa baris.

Kemudian dieksekusi pernyataan pilih dengan row_number()fungsi.

Pikiran saya adalah, karena fungsi row_number hanya diperkenalkan pada tahun 2005, ini akan membuat kesalahan dalam mode compat 80.

Tapi yang mengejutkan saya ini bekerja dengan baik. Maka, tentu saja, aturan compat hanya dievaluasi setelah Anda 'menyimpan sesuatu'. Jadi saya membuat proc tersimpan untuk pernyataan row_number saya.

Pembuatan proc yang disimpan berjalan dengan baik dan saya bisa menjalankannya dengan sempurna dan mendapatkan hasil.

Bisakah seseorang membantu saya untuk lebih memahami cara kerja mode kompatibilitas? Pemahaman saya jelas cacat.

suplex
sumber

Jawaban:

66

Dari dokumen :

Menetapkan perilaku basis data tertentu agar kompatibel dengan versi SQL Server yang ditentukan.
...
Tingkat kompatibilitas hanya menyediakan kompatibilitas sebagian dengan versi SQL Server sebelumnya. Gunakan tingkat kompatibilitas sebagai bantuan migrasi sementara untuk mengatasi perbedaan versi dalam perilaku yang dikendalikan oleh pengaturan tingkat kompatibilitas yang relevan.

Dalam interpretasi saya, mode kompatibilitas adalah tentang perilaku dan penguraian sintaks, bukan untuk hal-hal seperti pengurai yang mengatakan, "Hei, Anda tidak dapat menggunakan ROW_NUMBER()!" Kadang-kadang tingkat kompatibilitas yang lebih rendah memungkinkan Anda untuk terus pergi dengan sintaks yang tidak lagi didukung, dan kadang-kadang mencegah Anda menggunakan konstruksi sintaksis baru. Dokumentasi berisi beberapa contoh eksplisit, tetapi berikut adalah beberapa demonstrasi:


Melewati fungsi bawaan sebagai argumen fungsi

Kode ini berfungsi di tingkat kompatibilitas 90+:

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

Tapi di 80 itu menghasilkan:

Msg 102, Level 15, Negara 1
Sintaksis salah dekat '('.

Masalah khusus di sini adalah bahwa di 80 Anda tidak diizinkan untuk melewatkan fungsi bawaan ke fungsi. Jika Anda ingin tetap dalam 80 mode kompatibilitas, Anda dapat mengatasi ini dengan mengatakan:

DECLARE @db_id INT = DB_ID();

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

Mengirimkan tipe tabel ke fungsi bernilai tabel

Mirip dengan di atas, Anda bisa mendapatkan kesalahan sintaksis saat menggunakan TVP dan mencoba meneruskannya ke fungsi bernilai tabel. Ini berfungsi di level compat modern:

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

Namun, ubah tingkat kompatibilitas menjadi 80, dan jalankan tiga baris terakhir lagi; Anda mendapatkan pesan kesalahan ini:

Msg 137, Level 16, State 1, Line 19
Harus mendeklarasikan variabel skalar "@foo".

Tidak benar-benar ada solusi yang baik dari atas kepala saya, selain meningkatkan level compat atau mendapatkan hasil dengan cara yang berbeda.


Menggunakan nama kolom yang memenuhi syarat dalam BERLAKU

Dalam 90 mode kompatibilitas dan ke atas, Anda dapat melakukan ini tanpa masalah:

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

Namun, dalam 80 mode kompatibilitas, kolom yang memenuhi syarat diserahkan ke fungsi menimbulkan kesalahan sintaksis umum:

Msg 102, Level 15, Negara 1
Sintaks salah dekat '.'


ORDER OLEH alias yang kebetulan cocok dengan nama kolom

Pertimbangkan pertanyaan ini:

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

Dalam 80 mode kompatibilitas, hasilnya adalah sebagai berikut:

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

Dalam 90 mode kompatibilitas, hasilnya sangat berbeda:

snmuloc_lla      all_columns
stcejbo_lla      all_objects
sretemarap_lla   all_parameters
...

Alasannya? Dalam 80 mode kompatibilitas, awalan tabel diabaikan seluruhnya, sehingga memesan dengan ekspresi yang ditentukan oleh alias dalam SELECTdaftar. Di tingkat kompatibilitas yang lebih baru, awalan tabel dipertimbangkan, jadi SQL Server akan benar-benar menggunakan kolom itu dalam tabel (jika ditemukan). Jika ORDER BYalias tidak ditemukan dalam tabel, tingkat kompatibilitas yang lebih baru tidak begitu memaafkan tentang ambiguitas. Pertimbangkan contoh ini:

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

Hasilnya dipesan oleh mynameekspresi dalam 80, karena sekali lagi awalan tabel diabaikan, tetapi di 90 itu menghasilkan pesan kesalahan ini:

Msg 207, Level 16, Negara 1, Baris 3
Nama kolom 'myname' tidak valid.

Ini semua dijelaskan juga dalam dokumentasi :

Saat mengikat referensi kolom dalam ORDER BYdaftar ke kolom yang ditentukan dalam SELECTdaftar, ambiguitas kolom diabaikan dan awalan kolom terkadang diabaikan. Ini dapat menyebabkan hasil diatur untuk kembali dalam urutan yang tidak terduga.

Misalnya, ORDER BYklausa dengan satu kolom dua bagian ( <table_alias>.<column>) yang digunakan sebagai referensi ke kolom dalam daftar SELECT diterima, tetapi tabel alias diabaikan. Pertimbangkan pertanyaan berikut.

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

Ketika dieksekusi, awalan kolom diabaikan dalam ORDER BY. Operasi penyortiran tidak terjadi pada kolom sumber yang ditentukan ( x.c1) seperti yang diharapkan; alih-alih itu terjadi pada turunanc1kolom yang didefinisikan dalam kueri. Rencana eksekusi untuk kueri ini menunjukkan bahwa nilai-nilai untuk kolom yang diturunkan dihitung pertama dan kemudian nilai yang dihitung diurutkan.


ORDER BY sesuatu yang tidak ada dalam daftar SELECT

Dalam 90 mode kompatibilitas, Anda tidak dapat melakukan ini:

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;

Hasil:

Msg 104, Level 16, Negara 1
ORDER BY item harus muncul dalam daftar pilih jika pernyataan berisi operator UNION, INTERSECT atau KECUALI.

Namun, dalam 80, Anda masih bisa menggunakan sintaks ini.


Tua, luar bergabung dengan icky

Mode 80 juga memungkinkan Anda untuk menggunakan sintaks gabungan luar yang lama dan usang ( *=/=*):

SELECT o.name, c.name
FROM sys.objects AS o, sys.columns AS c
WHERE o.[object_id] *= c.[object_id];

Di SQL Server 2008/2008 R2, jika Anda berada di 90 atau lebih besar, Anda mendapatkan pesan verbose ini:

Msg 4147, Level 15, Negara 1
Permintaan menggunakan operator bergabung luar non-ANSI (" *=" atau " =*"). Untuk menjalankan kueri ini tanpa modifikasi, harap setel tingkat kompatibilitas untuk database saat ini ke 80, menggunakan opsi SET COMPATIBILITY_LEVEL dari ALTER DATABASE. Sangat disarankan untuk menulis ulang kueri menggunakan operator join luar ANSI (LEFT OUTER JOIN, RIGHT OUTER JOIN). Dalam versi SQL Server yang akan datang, operator gabungan non-ANSI tidak akan didukung bahkan dalam mode kompatibilitas-mundur.

Dalam SQL Server 2012, ini sama sekali tidak lagi sintaks yang valid, dan menghasilkan yang berikut:

Msg 102, Level 15, Negara 1, Baris 3
Sintaks salah dekat '* ='.

Tentu saja dalam SQL Server 2012 Anda tidak dapat lagi mengatasi masalah ini menggunakan tingkat kompatibilitas, karena 80 tidak lagi didukung. Jika Anda memutakhirkan basis data dalam mode compat 80 (dengan pemutakhiran di tempat, lepas / pasang, cadangan / kembalikan, pengiriman log, mirroring, dll.) Secara otomatis akan ditingkatkan menjadi 90 untuk Anda.


Petunjuk tabel tanpa DENGAN

Dalam mode 80 compat, Anda dapat menggunakan yang berikut dan petunjuk tabel akan diamati:

SELECT * FROM dbo.whatever NOLOCK; 

Dalam 90+, itu NOLOCKbukan lagi petunjuk tabel, ini adalah alias. Kalau tidak, ini akan berhasil:

SELECT * FROM dbo.whatever AS w NOLOCK;

Tetapi tidak:

Msg 1018, Level 15, Negara 1
Sintaksis salah dekat 'NOLOCK'. Jika ini dimaksudkan sebagai bagian dari petunjuk tabel, kata kunci WITH dan tanda kurung sekarang diperlukan. Lihat SQL Server Books Online untuk sintaksis yang tepat.

Sekarang, untuk membuktikan bahwa perilaku tidak diamati dalam contoh pertama ketika dalam mode 90 compat, gunakan AdventureWorks (pastikan itu dalam level compat yang lebih tinggi) dan jalankan yang berikut:

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;

Yang ini sangat bermasalah karena perilaku berubah tanpa pesan kesalahan atau bahkan kesalahan. Dan itu juga sesuatu yang bahkan tidak bisa dilihat oleh penasihat peningkatan dan alat-alat lain, karena untuk semua yang tahu, itu adalah tabel alias.


Konversi yang melibatkan jenis tanggal / waktu baru

Tipe tanggal / waktu baru yang diperkenalkan dalam SQL Server 2008 (misalnya datedan datetime2) mendukung rentang yang jauh lebih besar daripada yang asli datetimedan smalldatetime). Konversi nilai-nilai eksplisit di luar rentang yang didukung akan gagal apa pun tingkat kompatibilitasnya, misalnya:

SELECT CONVERT(SMALLDATETIME, '00010101');

Hasil:

Msg 242, Level 16, Negara 3
Konversi tipe data varchar ke tipe data waktu-kecil menghasilkan nilai di luar kisaran.

Namun, konversi tersirat akan bekerja sendiri di tingkat kompatibilitas yang lebih baru. Misalnya ini akan bekerja di 100+:

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

Namun di 80 (dan juga di 90), ia menghasilkan kesalahan yang sama seperti di atas:

Msg 242, Level 16, Negara 3
Konversi tipe data varchar ke tipe data datetime menghasilkan nilai di luar kisaran.


Klausa redundansi untuk pemicu

Ini adalah skenario yang tidak jelas yang muncul di sini . Dalam 80 mode kompatibilitas, ini akan berhasil:

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

Di 90 kompatibilitas dan lebih tinggi, ini tidak lagi diuraikan, dan sebagai gantinya Anda mendapatkan pesan kesalahan berikut:

Msg 1034, Level 15, Status 1, Prosedur tx
Kesalahan sintaks: Spesifikasi duplikat dari tindakan "UPDATE" dalam deklarasi pemicu.


PIVOT / UNPIVOT

Beberapa bentuk sintaks tidak akan berfungsi di bawah 80 (tetapi berfungsi dengan baik di 90+):

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

Ini menghasilkan:

Msg 156, Level 15, Negara 1
Sintaksis salah di dekat kata kunci 'untuk'.

Untuk beberapa solusi, termasuk CROSS APPLY, lihat jawaban ini .


Fungsi bawaan baru

Coba gunakan fungsi baru seperti TRY_CONVERT()dalam database dengan tingkat kompatibilitas <110. Mereka sama sekali tidak dikenali di sana.

SELECT TRY_CONVERT(INT, 1);

Hasil:

Msg 195, Level 15, Negara 10
'TRY_CONVERT' bukan nama fungsi bawaan yang dikenali.


Rekomendasi

Gunakan hanya 80 mode kompatibilitas jika Anda benar-benar membutuhkannya. Karena itu tidak akan lagi tersedia di versi berikutnya setelah 2008 R2, hal terakhir yang ingin Anda lakukan adalah menulis kode di level ini, bergantung pada perilaku yang Anda lihat, dan kemudian memiliki sejumlah kerusakan ketika Anda tidak bisa lagi gunakan level compat itu. Berpikir maju ke depan dan jangan mencoba melukis diri sendiri dengan membeli waktu untuk terus menggunakan sintaks yang sudah usang.

Aaron Bertrand
sumber
1
Jelas itu jawaban yang lebih baik dari saya!
Max Vernon
Terima kasih banyak atas jawaban yang rumit ini, Aaron! Dan untuk memperbaiki banyak kesalahan ejaan saya.
Souplex
1
Catatan kompatibilitas SQL Server 2014 dapat ditemukan di sini: msdn.microsoft.com/en-us/library/bb510680(v=sql.120).aspx
Josh Gallagher
9

Level Kompatibilitas hanya hadir untuk memungkinkan migrasi terkontrol dari versi SQL Server sebelumnya. Compat Level 90 tidak menghalangi penggunaan fitur-fitur baru, itu hanya berarti aspek-aspek tertentu dari database dipertahankan dengan cara yang kompatibel dengan cara SQL Server 2005 bekerja.

Lihat http://msdn.microsoft.com/en-us/library/bb510680.aspx untuk info lebih lanjut.

Max Vernon
sumber