นี่เป็นข้อผิดพลาดในการทำให้เป็นมาตรฐานของโครงการโดยใช้เคียวรีย่อยภายในนิพจน์เคสด้วยฟังก์ชันที่ไม่ได้กำหนดค่าไว้
ในการอธิบายเราต้องจดบันทึกสองสิ่งไว้ล่วงหน้า:
- SQL Server ไม่สามารถรัน subqueries โดยตรงดังนั้นพวกเขาจะคลี่เสมอหรือแปลงเป็นใช้
- ซีแมนทิกส์
CASE
นั้นเป็นTHEN
นิพจน์ที่ควรได้รับการประเมินถ้าWHEN
ประโยคส่งคืนจริง
เคียวรีย่อย (trivial) ที่แนะนำในกรณีที่เป็นปัญหาจึงส่งผลให้ตัวดำเนินการใช้ (การรวมลูปซ้อนกัน) เพื่อให้เป็นไปตามข้อกำหนดที่สอง SQL Server จะวางนิพจน์dbo.test6(1) + dbo.test6(2)
ทางด้านในของการนำไปใช้:
[Expr1000] = Scalar Operator([dbo].[test6]((1))+[dbo].[test6]((2)))
... ด้วยCASE
ความหมายที่ได้รับเกียรติจากภาคการส่งผ่านในการเข้าร่วม:
[@i]=(1) OR [@i]=(2) OR IsFalseOrNull [@i]=(3)
ด้านในของลูปจะได้รับการประเมินก็ต่อเมื่อเงื่อนไขการส่งผ่านประเมินว่าเป็นเท็จ (หมายถึง@i = 3
) ทั้งหมดนี้ถูกต้องแล้ว Compute เกลาต่อไปลูปซ้อนกันเข้าร่วมยังได้รับเกียรตินิยมCASE
ความหมายอย่างถูกต้อง:
[Expr1001] = Scalar Operator(CASE WHEN [@i]=(1) THEN (1) ELSE CASE WHEN [@i]=(2) THEN (2) ELSE CASE WHEN [@i]=(3) THEN [Expr1000] ELSE NULL END END END)
ปัญหาคือว่าขั้นตอนการทำให้เป็นมาตรฐานโครงการของการรวบรวมแบบสอบถามเห็นว่าExpr1000
ไม่เกี่ยวข้องและกำหนดว่ามันจะปลอดภัย ( ผู้บรรยาย: มันไม่ใช่ ) เพื่อย้ายออกนอกวง:
[Expr1000] = Scalar Operator([dbo].[test6]((1))+[dbo].[test6]((2)))
สิ่งนี้จะแบ่ง * ความหมายที่นำมาใช้โดยภาคแสดงการส่งผ่านดังนั้นฟังก์ชันจะถูกประเมินเมื่อไม่ควรเป็นและผลลัพธ์ลูปไม่สิ้นสุด
คุณควรรายงานข้อผิดพลาดนี้ วิธีหลีกเลี่ยงปัญหาคือการป้องกันไม่ให้นิพจน์ถูกย้ายออกนอกการสมัครโดยทำให้มันสัมพันธ์กัน (เช่นรวมถึง@i
การแสดงออก) แต่นี่เป็นการแฮ็กแน่นอน มีวิธีปิดใช้งานการทำให้เป็นมาตรฐานของโครงการ แต่ฉันถูกถามก่อนที่จะไม่เปิดเผยต่อสาธารณะดังนั้นฉันจะไม่ทำ
ปัญหานี้ไม่ได้เกิดขึ้นใน SQL Server 2019 เมื่อฟังก์ชันสเกลาร์ถูก inlineเนื่องจากลอจิกอินไลน์ทำงานโดยตรงบนต้นไม้ที่แยกวิเคราะห์ (ก่อนที่จะทำการปรับสภาพโครงการ) ตรรกะง่าย ๆ ในคำถามสามารถทำให้ง่ายขึ้นได้โดยตรรกะอินไลน์ที่ไม่ใช่แบบเรียกซ้ำ:
[Expr1019] = (Scalar Operator((1)))
[Expr1045] = Scalar Operator(CONVERT_IMPLICIT(int,CONVERT_IMPLICIT(int,[Expr1019],0)+(2),0))
... ซึ่งคืนค่า 3
อีกวิธีในการอธิบายปัญหาหลักคือ:
-- Not schema bound to make it non-det
CREATE OR ALTER FUNCTION dbo.Error()
RETURNS integer
-- WITH INLINE = OFF -- SQL Server 2019 only
AS
BEGIN
RETURN 1/0;
END;
GO
DECLARE @i integer = 1;
SELECT
CASE
WHEN @i = 1 THEN 1
WHEN @i = 2 THEN 2
WHEN @i = 3 THEN (SELECT dbo.Error()) -- 'subquery'
ELSE NULL
END;
สร้างซ้ำบนบิลด์ล่าสุดของทุกรุ่นตั้งแต่ 2008 R2 ถึง 2019 CTP 3.0
ตัวอย่างเพิ่มเติม (ไม่มีฟังก์ชันสเกลาร์) จัดทำโดยMartin Smith :
SELECT IIF(@@TRANCOUNT >= 0, 1, (SELECT CRYPT_GEN_RANDOM(4)/ 0))
สิ่งนี้มีองค์ประกอบสำคัญทั้งหมดที่จำเป็น:
CASE
(ดำเนินการภายในเป็นScaOp_IIF
)
- ฟังก์ชั่นที่ไม่ได้กำหนดไว้ (
CRYPT_GEN_RANDOM
)
- แบบสอบถามย่อยในสาขาที่ไม่ควรดำเนินการ (
(SELECT ...)
)
* อย่างเคร่งครัดการเปลี่ยนแปลงข้างต้นอาจยังคงถูกต้องหากการประเมินผลExpr1000
ถูกเลื่อนออกไปอย่างถูกต้องเนื่องจากมีการอ้างอิงโดยการก่อสร้างที่ปลอดภัยเท่านั้น:
[Expr1002] = Scalar Operator(CASE WHEN [@i]=(1) THEN (1) ELSE CASE WHEN [@i]=(2) THEN (2) ELSE CASE WHEN [@i]=(3) THEN [Expr1000] ELSE NULL END END END)
... แต่สิ่งนี้ต้องใช้การตั้งค่าสถานะForceOrderภายใน(ไม่ใช่คำใบ้แบบสอบถาม) ซึ่งไม่ได้ตั้งค่าไว้ ไม่ว่าในกรณีใดการใช้ตรรกะที่ใช้โดยการทำให้เป็นมาตรฐานของโครงการไม่ถูกต้องหรือไม่สมบูรณ์
รายงานข้อผิดพลาดในไซต์ Azure Feedback สำหรับ SQL Server