Saat menerapkan UNPIVOT
fungsi ke data yang tidak dinormalisasi, SQL Server mensyaratkan bahwa tipe data dan panjangnya harus sama. Saya mengerti mengapa datatype harus sama tetapi mengapa UNPIVOT membutuhkan panjangnya harus sama?
Katakanlah saya memiliki data sampel berikut yang harus saya batalkan 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');
Jika saya mencoba untuk UNPIVOT Firstname
dan Lastname
kolom yang mirip dengan:
select PersonId, ColumnName, Value
from People
unpivot
(
Value
FOR ColumnName in (FirstName, LastName)
) unpiv;
SQL Server menghasilkan kesalahan:
Msg 8167, Level 16, State 1, Line 6
Jenis kolom "Nama belakang" bertentangan dengan jenis kolom lain yang ditentukan dalam daftar UNPIVOT.
Untuk menyelesaikan kesalahan, kita harus menggunakan subquery untuk terlebih dahulu membuat Lastname
kolom dengan panjang yang sama seperti 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;
Lihat SQL Fiddle dengan Demo
Sebelum UNPIVOT diperkenalkan di SQL Server 2005, saya akan menggunakan SELECT
with UNION ALL
untuk membatalkan penguncian firstname
/ lastname
kolom dan kueri akan berjalan tanpa perlu mengonversi kolom ke panjang yang sama:
select personid, 'firstname' ColumnName, firstname value
from People
union all
select personid, 'LastName', LastName
from People;
Lihat SQL Fiddle dengan Demo .
Kami juga dapat berhasil mem-undivot data menggunakan CROSS APPLY
tanpa memiliki panjang yang sama pada tipe data:
select PersonId, columnname, value
from People
cross apply
(
select 'firstname', firstname union all
select 'lastname', lastname
) c (columnname, value);
Lihat SQL Fiddle dengan Demo .
Saya telah membaca MSDN tapi saya tidak menemukan penjelasan alasan untuk memaksa panjang pada datatype menjadi sama.
Apa logika di balik yang membutuhkan panjang yang sama saat menggunakan UNPIVOT?
sumber
Jawaban:
Pertanyaan ini hanya dapat benar-benar dijawab oleh orang-orang yang bekerja pada implementasi
UNPIVOT
. Anda mungkin dapat memperoleh ini dengan menghubungi mereka untuk mendapatkan dukungan . Berikut ini adalah pemahaman saya tentang alasan tersebut, yang mungkin tidak 100% akurat:T-SQL berisi sejumlah contoh semantik aneh dan perilaku kontra-intuitif lainnya. Beberapa di antaranya pada akhirnya akan hilang sebagai bagian dari siklus penghinaan, tetapi yang lain mungkin tidak pernah 'ditingkatkan' atau 'diperbaiki'. Terlepas dari hal lain, ada aplikasi yang bergantung pada perilaku ini, sehingga kompatibilitas ke belakang harus dipertahankan.
Aturan untuk konversi tersirat, dan derivasi jenis ekspresi akun untuk proporsi signifikan dari keanehan yang disebutkan di atas. Saya tidak iri pada penguji yang harus memastikan bahwa perilaku aneh (dan sering tidak berdokumen) dipertahankan (di bawah semua kombinasi
SET
nilai sesi dan sebagainya) untuk versi baru.Karena itu, tidak ada alasan untuk tidak melakukan perbaikan, dan menghindari kesalahan di masa lalu, ketika memperkenalkan fitur bahasa baru (dengan jelas tidak ada bagasi kompatibilitas ke belakang). Fitur baru seperti ekspresi tabel umum rekursif (seperti yang disebutkan oleh Andriy M dalam komentar) dan
UNPIVOT
bebas untuk memiliki semantik yang relatif waras dan aturan yang jelas.Akan ada berbagai pandangan tentang apakah termasuk panjang dalam jenis terlalu banyak mengetik secara eksplisit, tetapi secara pribadi saya menyambutnya. Dalam pandangan saya, jenis
varchar(25)
danvarchar(50)
yang tidak sama, lebih daridecimal(8)
dandecimal(10)
yang. Konversi jenis casing khusus mempersulit hal-hal yang tidak perlu dan tidak menambah nilai nyata, menurut saya.Orang bisa berpendapat bahwa hanya konversi implisit yang mungkin kehilangan data yang harus dinyatakan secara eksplisit, tetapi ada juga kasus tepi di sana. Pada akhirnya, konversi akan diperlukan, jadi sebaiknya kita membuatnya eksplisit.
Jika konversi implisit dari
varchar(25)
kevarchar(50)
diizinkan, itu hanya akan menjadi konversi implisit lain (kemungkinan besar tersembunyi), dengan semua kasus tepi aneh yang biasa danSET
pengaturan sensitivitas. Mengapa tidak menjadikan implementasi sesederhana dan se eksplisit mungkin? (Namun, tidak ada yang sempurna, dan memalukan bahwa bersembunyivarchar(25)
danvarchar(50)
di dalam asql_variant
diperbolehkan.)Menulis ulang
UNPIVOT
denganAPPLY
danUNION ALL
menghindari perilaku tipe (lebih baik) karena aturanUNION
tunduk pada kompatibilitas ke belakang, dan didokumentasikan dalam Books Online sebagai memungkinkan berbagai jenis selama mereka dapat dibandingkan menggunakan konversi implisit (yang aturan misterius dari prioritas tipe data digunakan, dan sebagainya).Solusinya melibatkan menjadi eksplisit tentang tipe data dan menambahkan konversi eksplisit jika perlu. Ini terlihat seperti kemajuan bagi saya :)
Salah satu cara untuk menulis solusi yang diketik secara eksplisit:
Contoh CTE rekursif:
Akhirnya, perhatikan bahwa penulisan ulang yang digunakan
CROSS APPLY
dalam pertanyaan tidak sama denganUNPIVOT
, karena ia tidak menolakNULL
atribut.sumber
The
UNPIVOT
Operator memanfaatkanIN
operator. The spesifikasi untuk operator IN (gambar di bawah) menunjukkan bahwa baiktest_expression
(dalam hal ini, di sebelah kiri dariIN
) dan masing-masingexpression
(di sisi kananIN
) harus tipe data yang sama. Berkat properti transitif kesetaraan, setiap ekspresi juga harus dari tipe data yang sama.sumber