พฤติกรรมที่แท้จริงของความเข้ากันได้ระดับ 80 คืออะไร?


47

มีใครให้ข้อมูลเชิงลึกที่ดีกว่าเกี่ยวกับคุณสมบัติโหมดความเข้ากันได้หรือไม่ มันมีพฤติกรรมแตกต่างจากที่ฉันคาดไว้

เท่าที่ฉันเข้าใจโหมดความเข้ากันได้มันเป็นเรื่องเกี่ยวกับความพร้อมใช้งานและการสนับสนุนโครงสร้างภาษาบางอย่างระหว่าง SQL Server รุ่นต่างๆ

ไม่ส่งผลกระทบต่อการทำงานภายในของรุ่นโปรแกรมฐานข้อมูล มันจะพยายามป้องกันการใช้คุณสมบัติและโครงสร้างที่ยังไม่พร้อมใช้งานในรุ่นก่อนหน้า

ฉันเพิ่งสร้างฐานข้อมูลใหม่ด้วยระดับ 80 ที่เข้ากันได้ใน SQL Server 2008 R2 สร้างตารางที่มีคอลัมน์ int หนึ่งคอลัมน์และสร้างด้วยแถวไม่กี่แถว

จากนั้นดำเนินการคำสั่งเลือกด้วยrow_number()ฟังก์ชั่น

ความคิดของฉันคือเนื่องจากฟังก์ชั่น row_number เพิ่งเปิดตัวในปี 2005 สิ่งนี้จะทำให้เกิดข้อผิดพลาดในโหมด 80 ที่เข้ากันได้

แต่ด้วยความประหลาดใจของฉันนี้ทำงานได้ดี จากนั้นแน่นอนว่ากฎการเข้ากันได้จะได้รับการประเมินก็ต่อเมื่อคุณ 'บันทึกบางอย่าง' ดังนั้นฉันจึงสร้าง proc ที่เก็บไว้สำหรับคำสั่ง row_number ของฉัน

การสร้าง 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 และพยายามส่งผ่านไปยังฟังก์ชันที่มีค่าเป็นตาราง ใช้งานได้ในระดับความเข้ากันได้ที่ทันสมัย:

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"

ไม่ได้มีวิธีแก้ปัญหาใด ๆ ที่ดีจากส่วนบนของหัวของฉันนอกเหนือจากการอัพเกรดระดับความเข้ากันได้หรือรับผลลัพธ์ที่แตกต่าง


ใช้ชื่อคอลัมน์ที่ผ่านการรับรองใน 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
ไวยากรณ์ไม่ถูกต้องใกล้กับ '.'


เรียงตามชื่อแทนที่ตรงกับชื่อคอลัมน์

พิจารณาคำถามนี้:

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รายการความคลุมเครือของคอลัมน์จะถูกละเว้นและบางครั้งคำนำหน้าคอลัมน์จะถูกละเว้น นี่อาจทำให้ชุดผลลัพธ์กลับมาเป็นลำดับที่ไม่คาดคิด

ตัวอย่างเช่นORDER BYอนุประโยคที่มีคอลัมน์สองส่วนเดียว ( <table_alias>.<column>) ที่ใช้เป็นการอ้างอิงไปยังคอลัมน์ในรายการ SELECT ได้รับการยอมรับ แต่นามแฝงของตารางจะถูกละเว้น พิจารณาคำถามต่อไปนี้ เมื่อดำเนินการคำนำหน้าคอลัมน์จะถูกละเว้นใน

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;

ผลลัพธ์:

ข่าวสารเกี่ยวกับ 104, ระดับ 16, สถานะ 1 เรียง
ลำดับตามรายการจะต้องปรากฏในรายการที่เลือกถ้าคำสั่งมีตัวดำเนินการ UNION, INTERSECT หรือ EXCEPT

อย่างไรก็ตามใน 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 ปีขึ้นไปคุณจะได้รับข้อความ verbose:

ข่าวสารเกี่ยวกับ 4147 ระดับ 15 สถานะ 1
แบบสอบถามใช้ตัวดำเนินการเข้าร่วมนอกที่ไม่ใช่ ANSI (" *=" หรือ " =*") หากต้องการเรียกใช้แบบสอบถามนี้โดยไม่มีการแก้ไขโปรดตั้งค่าระดับความเข้ากันได้สำหรับฐานข้อมูลปัจจุบันเป็น 80 โดยใช้ตัวเลือก SET COMPATIBILITY_LEVEL ของ ALTER DATABASE ขอแนะนำอย่างยิ่งให้เขียนแบบสอบถามใหม่โดยใช้ตัวดำเนินการเข้าร่วมด้านนอกของ ANSI (ซ้ายเข้าร่วมด้านขวาเข้าร่วมด้านนอก) ใน SQL Server รุ่นอนาคตตัวดำเนินการเข้าร่วมที่ไม่ใช่ ANSI จะไม่ได้รับการสนับสนุนแม้ในโหมดความเข้ากันได้แบบย้อนหลัง

ใน SQL Server 2012 นี่ไม่ใช่ไวยากรณ์ที่ถูกต้องอีกต่อไปและให้ผลดังนี้:

ข่าวสารเกี่ยวกับ 102 ระดับ 15 สถานะ 1 บรรทัด 3
ไวยากรณ์ไม่ถูกต้องใกล้ '* ='

แน่นอนใน SQL Server 2012 คุณไม่สามารถแก้ไขปัญหานี้ได้โดยใช้ระดับความเข้ากันได้อีกต่อไปเนื่องจาก 80 ไม่ได้รับการสนับสนุนอีกต่อไป หากคุณอัพเกรดฐานข้อมูลในโหมด 80 compat (โดยการอัพเกรดในสถานที่, ถอด / แนบ, สำรอง / กู้คืน, บันทึกการจัดส่ง, มิเรอร์และอื่น ๆ ) ระบบจะอัปเกรดเป็น 90 สำหรับคุณโดยอัตโนมัติ


คำใบ้ตารางโดยไม่ต้องด้วย

ในโหมดที่เข้ากัน 80 คุณสามารถใช้สิ่งต่อไปนี้และสังเกตคำใบ้ของตาราง:

SELECT * FROM dbo.whatever NOLOCK; 

ใน 90+ นั่นNOLOCKไม่ใช่คำใบ้ตารางอีกต่อไปมันเป็นนามแฝง มิฉะนั้นจะใช้งานได้:

SELECT * FROM dbo.whatever AS w NOLOCK;

แต่มันไม่ได้:

เกี่ยวกับ 1018 ระดับ 15 สถานะ 1
ไวยากรณ์ไม่ถูกต้องใกล้ 'NOLOCK' หากสิ่งนี้มีวัตถุประสงค์เพื่อเป็นส่วนหนึ่งของคำใบ้ของตารางตอนนี้จำเป็นต้องใช้คำหลัก A และวงเล็บ ดู SQL Server Books Online สำหรับไวยากรณ์ที่เหมาะสม

ตอนนี้เพื่อพิสูจน์ว่าพฤติกรรมดังกล่าวไม่ได้รับการสังเกตในตัวอย่างแรกเมื่ออยู่ในโหมดใช้งานร่วมกัน 90 โหมดให้ใช้ AdventureWorks (ตรวจสอบให้แน่ใจว่าอยู่ในระดับที่เข้ากันได้สูงกว่า) และเรียกใช้สิ่งต่อไปนี้:

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

อัตราผลตอบแทน:

ข่าวสารเกี่ยวกับ 242, ระดับ 16, สถานะ 3
การแปลงชนิดข้อมูล varchar เป็นชนิดข้อมูลขนาดเล็กในเวลาทำการส่งผลให้มีค่านอกช่วง

อย่างไรก็ตามการแปลงโดยนัยจะทำงานในระดับความเข้ากันได้ที่ใหม่กว่า เช่นนี้จะทำงานใน 100+:

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

แต่ใน 80 (และ 90) ก็ให้ข้อผิดพลาดที่คล้ายกันดังกล่าวข้างต้น:

เกี่ยวกับข่าวสาร 242 ระดับ 16 สถานะ 3
การแปลงชนิดข้อมูล varchar เป็นชนิดข้อมูล datetime ส่งผลให้มีค่านอกช่วง


ซ้ำซ้อนสำหรับส่วนคำสั่งในทริกเกอร์

นี่คือสถานการณ์ที่คลุมเครือที่ขึ้นมาที่นี่ ในโหมดความเข้ากันได้ 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" ในการประกาศทริกเกอร์


PIVOT / Unpivot

รูปแบบของไวยากรณ์บางรูปแบบจะไม่ทำงานภายใต้ 80 (แต่ทำงานได้ดีใน 90+):

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

อัตราผลตอบแทนนี้:

ข่าวสารเกี่ยวกับ 156, ระดับ 15, สถานะ 1
ไวยากรณ์ไม่ถูกต้องใกล้กับคำหลัก 'สำหรับ'

สำหรับวิธีการแก้ปัญหาบางอย่างรวมทั้งCROSS APPLYดูคำตอบเหล่านี้


ฟังก์ชั่นใหม่ในตัว

ลองใช้ฟังก์ชั่นใหม่ ๆ เช่นTRY_CONVERT()ในฐานข้อมูลที่มีระดับความเข้ากันได้ <110 พวกมันไม่ได้รับการยอมรับเลย

SELECT TRY_CONVERT(INT, 1);

ผลลัพธ์:

ข่าวสารเกี่ยวกับ 195, ระดับ 15, สถานะ 10
'TRY_CONVERT' ไม่ใช่ชื่อฟังก์ชันในตัวที่ได้รับการยอมรับ


คำแนะนำ

ใช้โหมดความเข้ากันได้ 80 โหมดเท่านั้นหากคุณต้องการใช้งานจริง เนื่องจากจะไม่สามารถใช้งานได้ในรุ่นถัดไปหลังจากปี 2008 R2 สิ่งสุดท้ายที่คุณต้องการทำคือการเขียนโค้ดในระดับการใช้งานร่วมกันนี้ขึ้นอยู่กับพฤติกรรมที่คุณเห็นแล้วมีการแตกหักทั้งหมดเมื่อคุณไม่สามารถ ใช้ระดับที่เข้ากันได้ คิดไปข้างหน้าและอย่าพยายามวาดมุมด้วยการซื้อเวลาเพื่อดำเนินการต่อโดยใช้ไวยากรณ์เก่าที่เลิกใช้แล้ว


1
เห็นได้ชัดว่าเป็นคำตอบที่ดีกว่าของฉัน!
Max Vernon

ขอบคุณมากสำหรับคำตอบที่ซับซ้อนนี้ Aaron! และสำหรับการแก้ไขข้อผิดพลาดการสะกดของฉันมากมาย
souplex

1
หมายเหตุความเข้ากันได้ของ SQL Server 2014 พบได้ที่นี่: msdn.microsoft.com/en-us/library/bb510680(v=sql.120).aspx
Josh Gallagher

9

ระดับความเข้ากันได้มีอยู่เพื่ออนุญาตการโยกย้ายแบบควบคุมจาก SQL Server รุ่นก่อนหน้าเท่านั้น ระดับความเข้ากันได้ 90 ไม่ได้ จำกัด การใช้คุณสมบัติใหม่ ๆ แต่ก็หมายถึงบางแง่มุมของฐานข้อมูลที่ถูกเก็บรักษาไว้ในลักษณะที่เข้ากันได้กับวิธีการทำงานของ SQL Server 2005

ดูhttp://msdn.microsoft.com/en-us/library/bb510680.aspxสำหรับข้อมูลเพิ่มเติม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.