SQL Server neden UNPIVOT kullanırken veri tipinin aynı olmasını gerektiriyor?


28

UNPIVOTİşlevi normalleştirilmemiş verilere uygularken , SQL Server veri türünün ve uzunluğunun aynı olmasını gerektirir. Veri tipinin neden aynı olması gerektiğini anlıyorum ama UNPIVOT neden aynı uzunlukta olmasını gerektiriyor?

Diyelim ki, kaldırmam gereken aşağıdaki örnek verilere sahibim:

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 Firstnameve Lastnamesütunlarını benzer şekilde denemeye çalışırsam :

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

SQL Server, hatayı oluşturur:

Mesaj 8167, Seviye 16, Durum 1, Satır 6

"Soyadı" sütununun türü, UNPIVOT listesinde belirtilen diğer sütunların türüyle çakışıyor.

Hatayı gidermek için, ilk önce Lastnameaynı uzunluğa sahip olacak şekilde sütunu yayınlamak için bir alt sorgu kullanmalıyız 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;

Demo ile SQL Fiddle'ı görün

UNPIVOT'un SQL Server 2005'te piyasaya sürülmesinden önce , / sütunları çıkarmak için SELECTile birlikte kullanacağım ve sorgu, sütunları aynı uzunluğa dönüştürmeye gerek kalmadan çalışacaktı:UNION ALLfirstnamelastname

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

Demo ile SQL Fiddle'a bakınız .

Ayrıca CROSS APPLY, veri türünde aynı uzunlukta olmadan verileri başarıyla çıkartabiliyoruz :

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

Demo ile SQL Fiddle'a bakınız .

MSDN aracılığıyla okudum ama veri türünün uzunluğu aynı olmaya zorlama nedenini açıklayan bir şey bulamadım.

UNPIVOT kullanırken aynı uzunluğu gerektiren arkasındaki mantık nedir?


4
(Muhtemelen ilgisiz ama ...) Bir özyinelemeli CTE'nin iki bölümünün sütun tiplerini karşılaştırırken aynı katılık uygulanır.
Andriy M,

Yanıtlar:


25

UNPIVOT kullanırken aynı uzunluğu gerektiren arkasındaki mantık nedir?

Bu soru ancak uygulamasında çalışan kişiler tarafından gerçekten cevaplanabilir UNPIVOT. Bunu destek için onlarla iletişim kurarak elde edebilirsiniz . Aşağıdaki,% 100 doğru olmayabilir mantık yürütme anlayışım:


T-SQL herhangi bir sayıda tuhaf semantik ve diğer sezgisel davranış örnekleri içerir. Bunlardan bazıları sonuçta, amortisman döngülerinin bir parçası olarak ortadan kalkacak, ancak diğerleri asla 'geliştirilemez' veya 'düzeltilemez'. Her şey dışında, bu davranışlara bağlı uygulamalar var, bu nedenle geriye dönük uyumluluk korunmalıdır.

Örtük dönüşümler ve ifade türü türetme kuralları yukarıda belirtilen tuhaflığın önemli bir bölümünü oluşturur. SETYeni sürümler için garip (ve genellikle belgelenmemiş) davranışların (tüm seans değerleri kombinasyonları vb. Altında) korunmasını sağlamak zorunda olan testçileri kıskanıyorum .

Bununla birlikte, yeni dil özellikleri sunarken (açıkça geriye dönük uyumluluk bagajı olmadan), iyileştirmeler yapmamak ve geçmiş hatalardan kaçınmak için iyi bir neden bulunmadığını söyledi. Özyinelemeli ortak tablo ifadeleri gibi yeni özellikler (bir yorumda Andriy M tarafından belirtildiği gibi ) ve UNPIVOTgöreceli olarak mantıklı semantik ve açıkça tanımlanmış kurallara sahip olma özgürlüğüne sahipti.

Tipe uzunluğun dahil edilmesinin çok açık bir şekilde yazılmakta olup olmadığı konusunda çeşitli görüşler olacaktır, ancak şahsen bunu memnuniyetle karşılıyorum. Benim görüşüme göre, türleri varchar(25)ve varchar(50)aynı değildir , daha fazla decimal(8)ve decimal(10)vardır. Özel mahfaza dize tipi dönüştürme işleri gereksiz yere zorlaştırıyor ve bence gerçek bir değer katmıyor.

Birisi, yalnızca veri kaybına neden olabilecek dönüşümlerin açıkça belirtilmesi gerektiğini, ancak orada da ileri düzeyde davalar bulunması gerektiğini savunabilir. Sonuçta, bir dönüşüm gerekli olacak, bu yüzden onu açık hale getirebiliriz.

Örtük dönüşüme varchar(25)geçiş varchar(50)yapılmasına izin verildiyse, tüm olağan garip kenar durumları ve SETayar hassasiyetleriyle birlikte başka bir (büyük olasılıkla gizli) örtük dönüşüm olacaktır . Neden uygulamayı mümkün olan en basit ve en açık şekilde yapmıyorsunuz? (Hiçbir şey ancak mükemmel olduğunu ve bir utanç olduğunu gizleme varchar(25)ve varchar(50)bir iç sql_variantizin verilir.)

Yeniden yazma UNPIVOTile APPLYve UNION ALLkuralları nedeniyle (daha iyi) tipi davranış önler UNIONgeriye doğru uyumluluk tabidir ve (örtük dönüştürme kullanarak o kadar uzun süre karşılaştırılabilir olarak farklı izin olarak Books Online belgelenmiştir olan veri tipi öncelik gizli kuralları ve benzeri kullanılır).

Geçici çözüm, veri türleri hakkında açık ve gerektiğinde açık dönüşümler eklemekten ibarettir. Bu bana ilerleme gibi görünüyor :)

Açıkça yazılmış geçici çözümü yazmanın bir yolu:

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;

Özyinelemeli CTE örneği:

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

Son olarak, CROSS APPLYsoruda kullanılan yeniden yazma özelliğinin aynısı olmadığını UNPIVOT, çünkü NULLözellikleri reddetmediğini unutmayın .


1

UNPIVOTOperatör kullanır INoperatörü. IN operatörü için özellikler (aşağıdaki ekran görüntüsü) hem göstermektedir test_expression(bu durumda, en solundaki IN) ve her expression(sağ tarafında IN) aynı veri türünde olmalıdır. Eşitliklerin geçişli özelliği sayesinde, her ifade de aynı veri türünde olmalıdır.

görüntü tanımını buraya girin


Doğru, veri türü gerekliliğini anlıyorum, ancak soru neden uzunlukların aynı olması gerektiğidir.
Taryn

Göz ardı ettim ve evet, IN işleci tipik olarak uzunluğu umursamıyor.
dev_etter

Uzunluk belirtme ihtiyacını göz ardı etmenize izin veren bir alternatif, her birini SQL_Variant
dev_etter 4:13
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.