Tại sao SQL Server yêu cầu độ dài kiểu dữ liệu giống nhau khi sử dụng UNPIVOT?


28

Khi áp dụng UNPIVOThàm cho dữ liệu không được chuẩn hóa, SQL Server yêu cầu kiểu dữ liệu và độ dài giống nhau. Tôi hiểu tại sao kiểu dữ liệu phải giống nhau nhưng tại sao UNPIVOT yêu cầu độ dài phải giống nhau?

Hãy nói rằng tôi có dữ liệu mẫu sau mà tôi cần hủy bỏ:

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

Nếu tôi cố gắng UNPIVOT FirstnameLastnamecác cột tương tự như:

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

Máy chủ SQL tạo ra lỗi:

Msg 8167, Cấp 16, Bang 1, Dòng 6

Loại cột "Họ" xung đột với loại cột khác được chỉ định trong danh sách UNPIVOT.

Để khắc phục lỗi, trước tiên chúng ta phải sử dụng truy vấn con để truyền Lastnamecột có cùng độ dài như Firstnamesau:

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;

Xem SQL Fiddle với bản demo

Trước khi UNPIVOT được giới thiệu trong SQL Server 2005, tôi sẽ sử dụng a SELECTvới UNION ALLđể hủy xoay firstname/ lastnamecột và truy vấn sẽ chạy mà không cần phải chuyển đổi các cột thành cùng độ dài:

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

Xem SQL Fiddle với Demo .

Chúng tôi cũng có thể hủy thành công dữ liệu bằng cách sử dụng CROSS APPLYmà không có cùng độ dài trên kiểu dữ liệu:

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

Xem SQL Fiddle với Demo .

Tôi đã đọc qua MSDN nhưng tôi không tìm thấy bất cứ điều gì giải thích lý do buộc độ dài trên kiểu dữ liệu là như nhau.

Logic đằng sau yêu cầu cùng độ dài khi sử dụng UNPIVOT là gì?


4
(Có thể không liên quan nhưng ...) Mức độ nghiêm ngặt tương tự được áp dụng khi so sánh các loại cột của hai phần của CTE đệ quy.
Andriy M

Câu trả lời:


25

Logic đằng sau yêu cầu cùng độ dài khi sử dụng UNPIVOT là gì?

Câu hỏi này chỉ có thể thực sự có thể trả lời được bởi những người làm việc trong việc thực hiện UNPIVOT. Bạn có thể có được điều này bằng cách liên hệ với họ để được hỗ trợ . Sau đây là sự hiểu biết của tôi về lý luận, có thể không chính xác 100%:


T-SQL chứa bất kỳ số lượng phiên bản ngữ nghĩa kỳ lạ và các hành vi phản trực giác khác. Một số trong số này cuối cùng sẽ biến mất như một phần của chu kỳ khấu hao, nhưng một số khác có thể không bao giờ được 'cải thiện' hoặc 'cố định'. Ngoài bất cứ điều gì khác, các ứng dụng tồn tại phụ thuộc vào các hành vi này, do đó khả năng tương thích ngược phải được bảo tồn.

Các quy tắc cho chuyển đổi ngầm định và dẫn xuất loại biểu thức cho một tỷ lệ đáng kể của sự kỳ lạ được đề cập ở trên. Tôi không ghen tị với những người thử nghiệm phải đảm bảo rằng các hành vi kỳ lạ (và thường không có giấy tờ) được bảo tồn (dưới tất cả các kết hợp của các SETgiá trị phiên và vv) cho các phiên bản mới.

Điều đó nói rằng, không có lý do chính đáng để không cải thiện và tránh những sai lầm trong quá khứ, khi giới thiệu các tính năng ngôn ngữ mới (rõ ràng là không có hành lý tương thích ngược). Các tính năng mới như biểu thức bảng chung đệ quy (như được đề cập bởi Andriy M trong một bình luận) và UNPIVOTđược tự do có ngữ nghĩa tương đối lành mạnh và các quy tắc được xác định rõ ràng.

Sẽ có một loạt các chế độ xem liệu có bao gồm độ dài trong loại đang gõ quá rõ ràng hay không, nhưng cá nhân tôi hoan nghênh nó. Theo quan điểm của tôi, các loại varchar(25)varchar(50)không giống nhau, bất kỳ hơn decimal(8)decimal(10)đang có. Theo tôi, chuyển đổi loại chuỗi vỏ đặc biệt làm phức tạp những thứ không cần thiết và không có giá trị thực, theo ý kiến ​​của tôi.

Người ta có thể lập luận rằng chỉ những chuyển đổi ngầm có thể làm mất dữ liệu nên được yêu cầu phải được nêu rõ ràng, nhưng cũng có những trường hợp cạnh đó. Cuối cùng, một chuyển đổi sẽ là cần thiết, vì vậy chúng tôi cũng có thể làm cho nó rõ ràng.

Nếu chuyển đổi ngầm định từ varchar(25)thành varchar(50)được cho phép, nó sẽ chỉ là một chuyển đổi ngầm định (rất có thể bị ẩn), với tất cả các trường hợp cạnh kỳ lạ thông thường và SETthiết lập độ nhạy. Tại sao không làm cho việc thực hiện đơn giản và rõ ràng nhất có thể? (Tuy nhiên, không có gì là hoàn hảo và thật xấu hổ khi ẩn varchar(25)varchar(50)bên trong a sql_variantđược cho phép.)

Viết lại hành vi UNPIVOTvới APPLYUNION ALLtránh hành vi loại (tốt hơn) vì các quy tắc UNIONcó thể tương thích ngược và được ghi lại trong Sách trực tuyến khi cho phép các loại khác nhau miễn là chúng có thể so sánh bằng cách sử dụng chuyển đổi ngầm định (theo đó các quy tắc phức tạp của loại dữ liệu ưu tiên được sử dụng, và như vậy).

Cách giải quyết liên quan đến việc rõ ràng về các loại dữ liệu và thêm các chuyển đổi rõ ràng khi cần thiết. Điều này có vẻ như tiến bộ với tôi :)

Một cách để viết cách giải quyết rõ ràng:

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;

Ví dụ CTE đệ quy:

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

Cuối cùng, lưu ý rằng việc viết lại sử dụng CROSS APPLYtrong câu hỏi không hoàn toàn giống với UNPIVOT, bởi vì nó không từ chối NULLcác thuộc tính.


1

Các UNPIVOTnhà khai thác sử dụng các INnhà điều hành. Các thông số kỹ thuật cho toán tử IN (ảnh chụp màn hình bên dưới) chỉ ra rằng cả test_expression(trong trường hợp này, ở bên trái của IN) và mỗi expression(ở phía bên phải của IN) phải cùng loại dữ liệu. Nhờ tính chất bắc cầu của đẳng thức, mỗi biểu thức cũng phải cùng loại dữ liệu.

nhập mô tả hình ảnh ở đây


Phải, tôi hiểu yêu cầu kiểu dữ liệu nhưng câu hỏi là tại sao độ dài phải giống nhau.
Taryn

Tôi đã bỏ qua điều đó, và vâng, toán tử IN thường không quan tâm đến độ dài.
dev_etter

Một cách khác cho phép bạn bỏ qua nhu cầu chỉ định độ dài là chọn từng độ dài như SQL_Variant: sqlfiddle.com/#!3/13b9a/2/0
dev_etter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.