เหตุใดเซิร์ฟเวอร์ที่เชื่อมโยงจึงมีข้อ จำกัด ถึง 10 สาขาในนิพจน์ CASE


19

ทำไมCASEการแสดงออกนี้:

SELECT CASE column 
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        ... c -> i
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END [col] 
FROM LinkedServer.database.dbo.table

สร้างผลลัพธ์นี้หรือไม่

ข้อความแสดงข้อผิดพลาด: ข่าวสารเกี่ยวกับ 8180, ระดับ 16, สถานะ 1, คำสั่ง Line 1
ไม่สามารถจัดเตรียมได้
ข่าวสารเกี่ยวกับ 125, ระดับ 15, สถานะ 4,
นิพจน์กรณีบรรทัด 1 อาจซ้อนอยู่ในระดับ 10 เท่านั้น

เห็นได้ชัดว่าไม่มีการCASEแสดงออกที่ซ้อนกันที่นี่แม้ว่าจะมีมากกว่า 10 "สาขา"

อีกเรื่องที่แปลก ฟังก์ชันมูลค่าตารางแบบอินไลน์นี้สร้างข้อผิดพลาดเดียวกัน:

ALTER FUNCTION [dbo].[fn_MyFunction]
(   
     @var varchar(20)
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT CASE column 
            WHEN 'a' THEN '1' 
            WHEN 'b' THEN '2' 
            ... c -> i
            WHEN 'j' THEN '10' 
            WHEN 'k' THEN '11'  
        END [col] 
    FROM LinkedServer.database.dbo.table
)

แต่ TVF หลายข้อความที่คล้ายกันทำงานได้ดี:

ALTER FUNCTION [dbo].[fn_MyFunction]
(   
    @var varchar(20)
)
RETURNS @result TABLE 
(
    value varchar(max)
)
AS
BEGIN
    INSERT INTO @result
    SELECT CASE column 
            WHEN 'a' THEN '1' 
            WHEN 'b' THEN '2' 
            ... c -> i
            WHEN 'j' THEN '10' 
            WHEN 'k' THEN '11'  
        END [col] 
    FROM LinkedServer.database.dbo.table

RETURN;
END

คำตอบ:


24

เห็นได้ชัดว่าไม่มีการCASEแสดงออกที่ซ้อนกันที่นี่

ไม่อยู่ในข้อความค้นหาไม่ใช่ แต่ตัวแยกวิเคราะห์จะขยายCASEนิพจน์เป็นรูปแบบซ้อนกันเสมอ:

SELECT CASE SUBSTRING(p.Name, 1, 1)
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        WHEN 'c' THEN '3' 
        WHEN 'd' THEN '4' 
        WHEN 'e' THEN '5' 
        WHEN 'f' THEN '6' 
        WHEN 'g' THEN '7' 
        WHEN 'h' THEN '8' 
        WHEN 'i' THEN '9' 
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END
FROM AdventureWorks2012.Production.Product AS p

แผนแบบสอบถามท้องถิ่น

แบบสอบถามนั้นเป็นโลคัล (ไม่มีเซิร์ฟเวอร์ที่เชื่อมโยง) และ Compute Scalar จะกำหนดนิพจน์ต่อไปนี้:

การแสดงออกของ CASE ที่ซ้อนกัน

สิ่งนี้ใช้ได้เมื่อดำเนินการแบบโลคัลเนื่องจากparserไม่เห็นCASEคำสั่งที่ซ้อนกันมากกว่า 10 ระดับ (แม้ว่ามันจะผ่านหนึ่งไปยังขั้นตอนต่อไปของการรวบรวมเคียวรีโลคัล)

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

อีกเรื่องที่แปลก ฟังก์ชันมูลค่าตารางแบบอินไลน์นี้สร้างข้อผิดพลาดเดียวกัน

ฟังก์ชันในบรรทัดถูกขยายเข้าแทนที่ข้อความค้นหาดั้งเดิมดังนั้นจึงไม่แปลกใจที่ผลลัพธ์ข้อผิดพลาดเดียวกันกับเซิร์ฟเวอร์ที่เชื่อมโยง

แต่ TVF หลายงบที่คล้ายกันทำงานได้ดี

คล้ายกัน แต่ไม่เหมือนกัน msTVF เกี่ยวข้องกับการแปลงโดยนัยvarchar(max)ซึ่งเกิดขึ้นเพื่อป้องกันการCASEแสดงออกที่ถูกส่งไปยังเซิร์ฟเวอร์ระยะไกล เนื่องจากCASEparser ถูกประเมินแบบโลคอล parser จึงไม่เห็น over-ซ้อนกันCASEและไม่มีข้อผิดพลาด หากคุณเปลี่ยนคำจำกัดความของตารางจากvarchar(max)เป็นชนิดของCASEผลลัพธ์ - varchar(2)- นิพจน์จะถูก remoting ด้วย msTVF และคุณจะได้รับข้อผิดพลาด

ในที่สุดข้อผิดพลาดเกิดขึ้นเมื่อมีการCASEประเมินผลที่ซ้อนกันโดยเซิร์ฟเวอร์ระยะไกล หากCASEไม่ได้รับการประเมินในตัวทำซ้ำแบบสอบถามระยะไกลจะไม่มีผลลัพธ์ข้อผิดพลาด ตัวอย่างเช่นต่อไปนี้รวมถึงCONVERTที่ไม่ได้รีโมตดังนั้นจึงไม่มีข้อผิดพลาดเกิดขึ้นแม้ว่าจะใช้เซิร์ฟเวอร์ที่ลิงก์:

SELECT CASE CONVERT(varchar(max), SUBSTRING(p.Name, 1, 1))
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        WHEN 'c' THEN '3' 
        WHEN 'd' THEN '4' 
        WHEN 'e' THEN '5' 
        WHEN 'f' THEN '6' 
        WHEN 'g' THEN '7' 
        WHEN 'h' THEN '8' 
        WHEN 'i' THEN '9' 
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END
FROM SQL2K8R2.AdventureWorks.Production.Product AS p

CASE ไม่ได้ถูกส่งใหม่


6

ลางสังหรณ์ของฉันคือการสืบค้นกำลังเขียนใหม่อยู่ที่ไหนสักแห่งระหว่างทางเพื่อให้มีCASEโครงสร้างที่แตกต่างกันเล็กน้อยเช่น

CASE WHEN column = 'a' THEN '1' ELSE CASE WHEN column = 'b' THEN '2' ELSE ...

ฉันเชื่อว่านี่เป็นข้อผิดพลาดในสิ่งที่ผู้ให้บริการเซิร์ฟเวอร์ที่เชื่อมโยงคุณใช้อยู่ (อันที่จริงอาจเป็นทั้งหมด - ฉันเคยเห็นมาหลายครั้งแล้ว) ฉันยังเชื่อว่าคุณไม่ควรกลั้นลมหายใจรอการแก้ไขไม่ว่าจะในฟังก์ชั่นการทำงานหรือข้อความแสดงข้อผิดพลาดที่สับสนซึ่งอธิบายพฤติกรรม - สิ่งนี้ได้รับการรายงานมาเป็นเวลานานเกี่ยวข้องกับเซิร์ฟเวอร์ที่เชื่อมโยง เซิร์ฟเวอร์ 2000) และส่งผลกระทบต่อคนน้อยกว่าข้อความแสดงข้อผิดพลาดที่ทำให้เกิดความสับสนซึ่งยังไม่ได้รับการแก้ไขหลังจากมีอายุการใช้งานที่ยาวนาน

เมื่อพอลชี้ให้เห็น SQL Server กำลังขยายCASEการแสดงออกของคุณไปยังความหลากหลายที่ซ้อนกันและเซิร์ฟเวอร์ที่เชื่อมโยงไม่ชอบ ข้อความแสดงข้อผิดพลาดทำให้เกิดความสับสน แต่เพียงเพราะการแปลงพื้นฐานของการแสดงออกไม่สามารถมองเห็นได้ทันที (หรือใช้งานง่ายในทางใดทางหนึ่ง)

วิธีแก้ปัญหาเดียว (นอกเหนือจากการเปลี่ยนฟังก์ชั่นที่คุณเพิ่มลงในคำถามของคุณ) คือการสร้างมุมมองหรือกระบวนงานที่เก็บไว้บนเซิร์ฟเวอร์ที่เชื่อมโยงและอ้างอิงว่าแทนที่จะส่งแบบสอบถามเต็มผ่านผู้ให้บริการเซิร์ฟเวอร์ที่เชื่อมโยง

อีกข้อหนึ่ง (สมมติว่าคำถามของคุณเป็นเรื่องง่ายและคุณต้องการให้สัมประสิทธิ์ตัวเลขของตัวอักษร az) มีดังนี้:

SELECT [col] = RTRIM(ASCII([column])-96)
FROM LinkedServer.database.dbo.table;

หากคุณต้องการให้สิ่งนี้ทำงานตามที่เป็นจริงฉันขอแนะนำให้คุณติดต่อฝ่ายสนับสนุนโดยตรงและเปิดเคส แต่ฉันไม่สามารถรับรองผลได้ - พวกเขาอาจให้การแก้ไขปัญหาที่คุณสามารถเข้าถึงได้ในหน้านี้


5

คุณสามารถรับรอบนี้โดย

SELECT COALESCE(
CASE SUBSTRING(p.Name, 1, 1)
    WHEN 'a' THEN '1' 
    WHEN 'b' THEN '2' 
    WHEN 'c' THEN '3' 
    WHEN 'd' THEN '4' 
    WHEN 'e' THEN '5' 
    WHEN 'f' THEN '6' 
    WHEN 'g' THEN '7' 
    WHEN 'h' THEN '8' 
    WHEN 'i' THEN '9' 
    ELSE NULL
END,
CASE SUBSTRING(p.Name, 1, 1)
    WHEN 'j' THEN '10' 
    WHEN 'k' THEN '11'  
END)
FROM SQL2K8R2.AdventureWorks.Production.Product AS p

2

วิธีแก้ปัญหาอื่นสำหรับปัญหานี้คือการใช้ตรรกะตามชุดแทนที่CASEนิพจน์ด้วยการรวมซ้าย (หรือนำไปใช้ภายนอก) ไปยังตารางอ้างอิง ( refในรหัสด้านล่าง) ซึ่งอาจเป็นแบบถาวรชั่วคราวหรือตาราง / CTE ที่ได้รับ หากต้องการสิ่งนี้ในหลาย ๆ แบบสอบถามและขั้นตอนฉันต้องการให้มันเป็นตารางถาวร:

SELECT ref.result_column AS [col] 
FROM LinkedServer.database.dbo.table AS t
  LEFT JOIN
    ( VALUES ('a',  '1'),
             ('b',  '2'), 
             ('c',  '3'),
             ---
             ('j', '10'),
             ('k', '11')
    ) AS ref (check_col, result_column) 
    ON ref.check_col = t.column ;

-4

วิธีหนึ่งในการหลีกเลี่ยงปัญหานี้คือการรวมการทดสอบไว้ในwhenข้อเช่น

case
  when SUBSTRING(p.Name, 1, 1) = 'a' THEN '1'
...

ไม่จริง ทั้งสองSELECT CASE v.V WHEN 'a' THEN 1 WHEN 'b' THEN 2 END FROM (VALUES ('a'), ('b')) AS v (V);และSELECT CASE WHEN v.V = 'a' THEN 1 WHEN v.V = 'b' THEN 2 END FROM (VALUES ('a'), ('b')) AS v (V);แปลเป็นแผนปฏิบัติการเดียวกันทั้งหมด (รู้สึกอิสระที่จะยืนยันว่าสำหรับตัวคุณเอง) โดยที่การแสดงออกของ CASE ถูกนิยามใหม่CASE WHEN [Union1002]='a' THEN (1) ELSE CASE WHEN [Union1002]='b' THEN (2) ELSE NULL END ENDด้วย - โดยมีการซ้อนกันตามที่คุณเห็น
Andriy M
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.