ทำไม SQL Server จำเป็นต้องใช้ความยาวประเภทข้อมูลเหมือนกันเมื่อใช้ UNPIVOT


28

เมื่อใช้UNPIVOTฟังก์ชันกับข้อมูลที่ไม่ได้ทำให้เป็นมาตรฐาน SQL Server ต้องการให้ประเภทข้อมูลและความยาวเหมือนกัน ฉันเข้าใจว่าทำไมประเภทข้อมูลต้องเหมือนกัน แต่ทำไม UNPIVOT จึงต้องมีความยาวเท่ากัน

สมมติว่าฉันมีข้อมูลตัวอย่างต่อไปนี้ที่ฉันต้องการยกเลิกการหมุน:

CREATE TABLE People
(
    PersonId int, 
    Firstname varchar(50), 
    Lastname varchar(25)
)

INSERT INTO People VALUES (1, 'Jim', 'Smith');
INSERT INTO People VALUES (2, 'Jane', 'Jones');
INSERT INTO People VALUES (3, 'Bob', 'Unicorn');

หากฉันพยายาม UNPIVOT FirstnameและLastnameคอลัมน์คล้ายกับ:

select PersonId, ColumnName, Value  
from People
unpivot
(
  Value 
  FOR ColumnName in (FirstName, LastName)
) unpiv;

SQL Server สร้างข้อผิดพลาด:

ข่าวสารเกี่ยวกับ 8167 ระดับ 16 สถานะ 1 สาย 6

ประเภทของคอลัมน์ "นามสกุล" ขัดแย้งกับประเภทของคอลัมน์อื่น ๆ ที่ระบุในรายการ UNPIVOT

ในการแก้ไขข้อผิดพลาดเราจะต้องใช้แบบสอบถามย่อยเพื่อเลือกLastnameคอลัมน์ก่อนเพื่อให้มีความยาวเท่ากับFirstname:

select PersonId, ColumnName, Value  
from
(
  select personid, 
    firstname, 
    cast(lastname as varchar(50)) lastname
  from People
) d
unpivot
(
  Value FOR 
  ColumnName in (FirstName, LastName)
) unpiv;

ดูSQL Fiddle พร้อมเดโม

ก่อนที่จะถูกนำ Unpivot ใน SQL Server 2005 ผมจะใช้SELECTกับUNION ALLการ Unpivot firstname/ lastnameคอลัมน์และแบบสอบถามจะทำงานได้โดยไม่ต้องแปลงคอลัมน์ความยาวเดียวกัน:

select personid, 'firstname' ColumnName, firstname value
from People
union all
select personid, 'LastName', LastName
from People;

ดูซอ SQL กับการสาธิต

นอกจากนี้เรายังสามารถยกเลิกการย้ายข้อมูลโดยใช้CROSS APPLYโดยไม่ต้องมีความยาวเท่ากันในประเภทข้อมูล:

select PersonId, columnname, value
from People
cross apply
(
    select 'firstname', firstname union all
    select 'lastname', lastname
) c (columnname, value);

ดูซอ SQL กับการสาธิต

ฉันอ่านผ่าน MSDN แล้ว แต่ฉันไม่พบสิ่งใดที่อธิบายเหตุผลในการบังคับให้ความยาวของประเภทข้อมูลนั้นเท่ากัน

ตรรกะเบื้องหลังที่ต้องใช้ความยาวเท่ากันเมื่อใช้ UNPIVOT คืออะไร


4
(อาจไม่เกี่ยวข้องกัน แต่ ... ) จะใช้ความเข้มงวดแบบเดียวกันเมื่อเปรียบเทียบประเภทคอลัมน์ของสองส่วนของ CTE แบบเรียกซ้ำ
Andriy M

คำตอบ:


25

ตรรกะเบื้องหลังที่ต้องใช้ความยาวเท่ากันเมื่อใช้ UNPIVOT คืออะไร

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


T-SQL มีจำนวนอินสแตนซ์ของความหมายแปลก ๆ และพฤติกรรมต่อต้านการใช้งานง่าย ๆ บางส่วนของสิ่งเหล่านี้จะหายไปในที่สุดเป็นส่วนหนึ่งของรอบการคัดค้าน แต่คนอื่น ๆ อาจไม่เคย 'ปรับปรุง' หรือ 'คงที่' นอกเหนือจากสิ่งอื่นแล้วแอปพลิเคชันมีอยู่ที่ขึ้นอยู่กับพฤติกรรมเหล่านี้ดังนั้นความเข้ากันได้แบบย้อนหลังจะต้องได้รับการเก็บรักษาไว้

กฎสำหรับการแปลงโดยนัยและการได้มาซึ่งประเภทของนิพจน์นั้นเป็นสัดส่วนที่สำคัญของความแปลกประหลาดที่กล่าวถึงข้างต้น ฉันไม่อิจฉาผู้ทดสอบที่ต้องตรวจสอบให้แน่ใจว่าพฤติกรรมแปลก ๆ (และบ่อยครั้งที่ไม่มีเอกสาร) จะได้รับการเก็บรักษาไว้ (ภายใต้การรวมSETค่าเซสชันและอื่น ๆ ) สำหรับเวอร์ชันใหม่

ที่กล่าวว่าไม่มีเหตุผลที่ดีที่จะไม่ทำการปรับปรุงและหลีกเลี่ยงข้อผิดพลาดที่ผ่านมาเมื่อมีการแนะนำคุณสมบัติภาษาใหม่ คุณลักษณะใหม่เช่นนิพจน์ตารางแบบเรียกซ้ำ (ตามที่Andriy Mกล่าวไว้ในความคิดเห็น) และUNPIVOTมีอิสระที่จะมีความหมายที่มีเหตุผลและกฎที่กำหนดไว้อย่างชัดเจน

จะมีช่วงของมุมมองว่าการรวมความยาวในประเภทนั้นเป็นการพิมพ์ที่ชัดเจนเกินไปหรือไม่ แต่โดยส่วนตัวแล้วฉันยินดีต้อนรับ ในมุมมองของฉันประเภทvarchar(25)และvarchar(50)จะไม่เหมือนกันมากกว่าdecimal(8)และdecimal(10)เป็น การแปลงชนิดสตริงสตริงแบบพิเศษทำให้สิ่งต่าง ๆ ยุ่งยากโดยไม่จำเป็นและไม่เพิ่มมูลค่าที่แท้จริงในความคิดของฉัน

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

หากการแปลงนัยจากvarchar(25)การvarchar(50)ได้รับอนุญาตมันก็จะเป็นอีก (ส่วนใหญ่มีแนวโน้มที่ซ่อนไว้) การแปลงนัยกับทุกกรณีขอบแปลกตามปกติและSETความไวของการตั้งค่า ทำไมไม่ทำให้การใช้งานง่ายและชัดเจนที่สุด (ไม่มีสิ่งใดที่สมบูรณ์แบบและเป็นความอัปยศที่อนุญาตvarchar(25)และซ่อนตัวอยู่varchar(50)ภายในsql_variant)

เขียนใหม่UNPIVOTด้วยAPPLYและUNION ALLหลีกเลี่ยงพฤติกรรมประเภท (ดีกว่า) เพราะกฎสำหรับUNIONเรื่องความเข้ากันได้ย้อนหลังและมีการบันทึกไว้ใน Books Online ว่าอนุญาตประเภทที่แตกต่างกันตราบใดที่พวกเขาเปรียบเทียบกันได้โดยใช้การแปลงโดยนัย ถูกนำมาใช้และอื่น ๆ )

วิธีแก้ปัญหาเกี่ยวข้องกับความชัดเจนเกี่ยวกับชนิดข้อมูลและเพิ่มการแปลงที่ชัดเจนในกรณีที่จำเป็น ดูเหมือนว่าจะเป็นความคืบหน้าสำหรับฉัน :)

วิธีหนึ่งในการเขียนวิธีแก้ปัญหาที่พิมพ์อย่างชัดเจน:

SELECT
    U.PersonId,
    U.ColumnName,
    U.Value
FROM dbo.People AS P
CROSS APPLY
(
    VALUES (CONVERT(varchar(50), Lastname))
) AS CA (Lastname)
UNPIVOT
(
    Value FOR
    ColumnName IN (P.Firstname, CA.Lastname)
) AS U;

ตัวอย่าง CTE แบบเรียกซ้ำ:

-- Fails
WITH R AS
(
    SELECT Dummy = 'A row'
    UNION ALL
    SELECT 'Another row'
    FROM R
    WHERE Dummy = 'A row'
)
SELECT Dummy
FROM R;

-- Succeeds
WITH R AS
(
    SELECT Dummy = CONVERT(varchar(11), 'A row')
    UNION ALL
    SELECT CONVERT(varchar(11), 'Another row')
    FROM R
    WHERE Dummy = 'A row'
)
SELECT Dummy
FROM R;

ท้ายที่สุดโปรดทราบว่าการเขียนCROSS APPLYซ้ำที่ใช้ในคำถามนั้นไม่เหมือนกับของUNPIVOTเพราะมันไม่ได้ปฏิเสธNULLคุณสมบัติ


1

UNPIVOTประกอบการใช้INประกอบการ ข้อกำหนดสำหรับผู้ประกอบการใน (ภาพด้านล่าง) แสดงให้เห็นว่าทั้งสองtest_expression(ในกรณีนี้บนซ้ายของIN) และแต่ละคนexpression(อยู่ทางด้านขวาของIN) ต้องเป็นชนิดข้อมูลเดียวกัน ต้องขอบคุณคุณสมบัติสกรรมกริยาของความเท่าเทียมกันการแสดงออกของแต่ละคนจะต้องเป็นชนิดข้อมูลเดียวกันเช่นกัน

ป้อนคำอธิบายรูปภาพที่นี่


ใช่ฉันเข้าใจความต้องการประเภทข้อมูล แต่คำถามคือทำไมความยาวจึงต้องเท่ากัน
Taryn

ฉันมองข้ามและใช่ผู้ประกอบการ IN มักไม่สนใจความยาว
dev_etter

อีกทางเลือกหนึ่งที่ช่วยให้คุณมองข้ามความต้องการในการระบุความยาวคือการส่งแต่ละรายการเป็น SQL_Variant: sqlfiddle.com/#!3/13b9a/2/0
dev_etter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.