Ini adalah bagian ke-9 dalam seri tentang ekspresi tabel bernama. Di Bagian 1 saya memberikan latar belakang untuk ekspresi tabel bernama, yang mencakup tabel turunan, ekspresi tabel umum (CTE), tampilan, dan fungsi nilai tabel sebaris (iTVF). Di Bagian 2, Bagian 3 dan Bagian 4 saya fokus pada tabel turunan. Di Bagian 5, Bagian 6, Bagian 7 dan Bagian 8 saya fokus pada CTE. Seperti yang saya jelaskan, tabel turunan dan CTE adalah ekspresi tabel bernama lingkup pernyataan. Setelah pernyataan yang mendefinisikan mereka selesai, mereka hilang.
Kami sekarang siap untuk melanjutkan ke cakupan ekspresi tabel bernama yang dapat digunakan kembali. Yaitu, yang dibuat sebagai objek dalam database, dan tetap di sana secara permanen kecuali dijatuhkan. Dengan demikian, mereka dapat diakses dan digunakan kembali oleh semua orang yang memiliki izin yang tepat. Tampilan dan iTVF termasuk dalam kategori ini. Perbedaan antara keduanya terutama karena yang pertama tidak mendukung parameter input dan yang terakhir mendukung.
Dalam artikel ini saya memulai cakupan pandangan. Seperti yang saya lakukan sebelumnya, pertama-tama saya akan fokus pada aspek logis, atau konseptual, dan kemudian melanjutkan ke aspek optimasi. Dengan artikel pertama tentang tampilan, saya ingin memulai dengan ringan, berfokus pada apa itu tampilan, menggunakan terminologi yang benar, dan membandingkan pertimbangan desain tampilan dengan tabel turunan dan CTE yang telah dibahas sebelumnya.
Dalam contoh saya, saya akan menggunakan database sampel yang disebut TSQLV5. Anda dapat menemukan skrip yang membuat dan mengisinya di sini, dan diagram ER-nya di sini.
Apa itu view?
Seperti biasa ketika membahas teori relasional, kita para praktisi SQL sering diberi tahu bahwa terminologi yang kita gunakan salah. Jadi, dalam semangat ini, langsung saja, saya akan mulai dengan mengatakan bahwa ketika Anda menggunakan istilah tabel dan tampilan , itu salah. Saya mempelajari ini dari Chris Date.
Ingatlah bahwa tabel adalah rekan SQL untuk suatu relasi (sedikit menyederhanakan diskusi seputar nilai dan variabel). Tabel bisa berupa tabel dasar yang didefinisikan sebagai objek dalam database, atau bisa berupa tabel yang dikembalikan oleh ekspresi—lebih khusus lagi, ekspresi tabel. Itu mirip dengan fakta bahwa suatu relasi bisa menjadi relasi yang dikembalikan dari ekspresi relasional. Ekspresi tabel bisa berupa kueri.
Sekarang, apa itu pemandangan? Ini adalah ekspresi tabel bernama, seperti CTE adalah ekspresi tabel bernama. Hanya saja seperti yang saya katakan, tampilan adalah ekspresi tabel bernama yang dapat digunakan kembali yang dibuat sebagai objek dalam database, dan dapat diakses oleh mereka yang memiliki izin yang tepat. Ini semua untuk mengatakan, tampilan adalah tabel. Ini bukan meja dasar, tapi tetap meja. Jadi, seperti mengatakan "persegi panjang dan persegi" atau "wiski dan Lagavulin" akan tampak aneh (kecuali Anda memiliki terlalu banyak Lagavulin!), menggunakan "tabel dan tampilan" juga tidak tepat.
Sintaks
Berikut sintaks T-SQL untuk pernyataan CREATE VIEW:
BUAT [ATAU ALTER] LIHAT [[ DENGAN
AS
[ DENGAN OPSI PERIKSA ]
[; ]
Pernyataan CREATE VIEW harus menjadi pernyataan pertama, dan satu-satunya, dalam kumpulan.
Perhatikan bahwa bagian CREATE OR ALTER diperkenalkan di SQL Server 2016 SP1, jadi jika Anda menggunakan versi yang lebih lama, Anda harus bekerja dengan pernyataan CREATE VIEW dan ALTER VIEW yang terpisah tergantung pada apakah objek sudah ada atau belum. Seperti yang mungkin Anda ketahui, mengubah objek yang sudah ada akan mempertahankan izin yang ditetapkan. Itulah salah satu alasan mengapa biasanya masuk akal untuk mengubah objek yang ada daripada menjatuhkan dan membuatnya kembali. Yang mengejutkan beberapa orang adalah bahwa mengubah tampilan tidak mempertahankan atribut tampilan yang ada; itu perlu ditentukan ulang jika Anda ingin mempertahankannya.
Berikut ini contoh definisi tampilan sederhana yang mewakili pelanggan AS:
USE TSQLV5; GO CREATE OR ALTER VIEW Sales.USACustomers AS SELECT custid, companyname FROM Sales.Customers WHERE country = N'USA'; GO
Dan inilah pernyataan yang menanyakan tampilan:
SELECT custid, companyname FROM Sales.USACustomers;
Di antara pernyataan yang membuat tampilan, dan pernyataan yang menanyakannya, Anda akan menemukan tiga elemen yang sama yang terlibat dalam pernyataan terhadap tabel turunan atau CTE:
- Ekspresi tabel bagian dalam (kueri bagian dalam tampilan)
- Nama tabel yang ditetapkan (nama tampilan)
- Pernyataan dengan kueri luar terhadap tampilan
Anda yang memiliki mata yang tajam akan memperhatikan bahwa sebenarnya ada dua ekspresi tabel yang terlibat di sini. Ada yang dalam (kueri bagian dalam tampilan), dan ada yang bagian luar (kueri dalam pernyataan yang bertentangan dengan tampilan). Dalam pernyataan dengan kueri terhadap tampilan, kueri itu sendiri adalah ekspresi tabel, dan setelah Anda menambahkan terminator, itu menjadi sebuah pernyataan. Ini mungkin terdengar pilih-pilih, tetapi jika Anda mengerti, dan menyebut sesuatu dengan nama yang benar, itu mencerminkan pengetahuan Anda. Dan bukankah itu bagus ketika Anda tahu bahwa Anda tahu?
Selain itu, semua persyaratan dari ekspresi tabel dalam tabel turunan dan CTE yang telah kita bahas sebelumnya dalam seri ini, berlaku untuk ekspresi tabel yang menjadi dasar tampilan. Sebagai pengingat, persyaratannya adalah:
- Semua kolom ekspresi tabel harus memiliki nama
- Semua nama kolom ekspresi tabel harus unik
- Baris ekspresi tabel tidak memiliki urutan
Jika Anda perlu menyegarkan pemahaman Anda tentang apa yang ada di balik persyaratan ini, lihat bagian “Ekspresi tabel adalah tabel” di Bagian 2 dari seri ini. Pastikan bahwa Anda secara khusus memahami bagian "tidak ada pesanan". Sebagai pengingat singkat, ekspresi tabel adalah tabel, dan karena itu tidak memiliki urutan. Itulah mengapa Anda tidak dapat membuat tampilan berdasarkan kueri dengan klausa ORDER BY, kecuali klausa ini ada untuk mendukung filter TOP atau OFFSET-FETCH. Dan bahkan dengan pengecualian ini yang memungkinkan kueri dalam memiliki klausa ORDER BY, Anda ingin mengingat bahwa jika kueri luar terhadap tampilan tidak memiliki klausa ORDER BY sendiri, Anda tidak mendapatkan jaminan bahwa kueri akan kembali baris dalam urutan tertentu, apalagi perilaku yang diamati. Ini sangat penting untuk dipahami!
Bersarang dan banyak referensi
Saat mendiskusikan pertimbangan desain tabel turunan dan CTE, saya membandingkan keduanya dalam hal referensi bersarang dan banyak. Sekarang mari kita lihat bagaimana tampilan tarif di departemen ini. Saya akan mulai dengan bersarang. Untuk tujuan ini, kami akan membandingkan kode yang mengembalikan tahun di mana lebih dari 70 pelanggan melakukan pemesanan menggunakan tabel turunan, CTE, dan tampilan. Anda sudah melihat kode dengan tabel turunan dan CTE di awal seri. Berikut kode yang menangani tugas menggunakan tabel turunan:
SELECT orderyear, numcusts FROM ( SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM ( SELECT YEAR(orderdate) AS orderyear, custid FROM Sales.Orders ) AS D1 GROUP BY orderyear ) AS D2 WHERE numcusts > 70;
Saya menunjukkan bahwa kelemahan utama yang saya lihat dengan tabel turunan di sini adalah fakta bahwa Anda menyarangkan definisi tabel turunan, dan ini dapat menyebabkan kerumitan dalam memahami, memelihara, dan memecahkan masalah kode tersebut.
Berikut kode yang menangani tugas yang sama menggunakan CTE:
WITH C1 AS ( SELECT YEAR(orderdate) AS orderyear, custid FROM Sales.Orders ), C2 AS ( SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM C1 GROUP BY orderyear ) SELECT orderyear, numcusts FROM C2 WHERE numcusts > 70;
Saya menunjukkan bahwa bagi saya ini terasa seperti kode yang jauh lebih jelas karena kurangnya bersarang. Anda dapat melihat setiap langkah dalam solusi dari awal hingga akhir secara terpisah di unitnya sendiri, dengan logika solusi mengalir dengan jelas dari atas ke bawah. Oleh karena itu, saya melihat opsi CTE sebagai peningkatan dari tabel turunan dalam hal ini.
Sekarang untuk dilihat. Ingat, salah satu manfaat utama tampilan adalah dapat digunakan kembali. Anda juga dapat mengontrol izin akses. Pengembangan unit yang terlibat sedikit lebih mirip dengan CTE dalam arti bahwa Anda dapat memusatkan perhatian Anda pada satu unit pada satu waktu dari awal hingga akhir. Selain itu, Anda memiliki fleksibilitas untuk memutuskan apakah akan membuat tampilan terpisah per unit dalam solusi, atau, mungkin hanya satu tampilan berdasarkan kueri yang melibatkan ekspresi tabel bernama cakupan pernyataan.
Anda akan menggunakan yang pertama ketika masing-masing unit harus dapat digunakan kembali. Berikut kode yang akan Anda gunakan dalam kasus seperti itu, membuat tiga tampilan:
-- Sales.OrderYears CREATE OR ALTER VIEW Sales.OrderYears AS SELECT YEAR(orderdate) AS orderyear, custid FROM Sales.Orders; GO -- Sales.YearlyCustCounts CREATE OR ALTER VIEW Sales.YearlyCustCounts AS SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM Sales.OrderYears GROUP BY orderyear; GO -- Sales.YearlyCustCountsMin70 CREATE OR ALTER VIEW Sales.YearlyCustCountsAbove70 AS SELECT orderyear, numcusts FROM Sales.YearlyCustCounts WHERE numcusts > 70; GO
Anda dapat mengkueri setiap tampilan secara independen, tetapi inilah kode yang akan Anda gunakan untuk mengembalikan apa yang menjadi tugas awal setelahnya.
SELECT orderyear, numcusts FROM Sales.YearlyCustCountsAbove70;
Jika ada persyaratan dapat digunakan kembali hanya untuk bagian terluar (apa tugas asli yang diperlukan), tidak ada kebutuhan nyata untuk mengembangkan tiga tampilan yang berbeda. Anda bisa membuat satu tampilan berdasarkan kueri yang melibatkan CTE atau tabel turunan. Inilah cara Anda melakukannya dengan kueri yang melibatkan CTE:
CREATE OR ALTER VIEW Sales.YearlyCustCountsAbove70 AS WITH C1 AS ( SELECT YEAR(orderdate) AS orderyear, custid FROM Sales.Orders ), C2 AS ( SELECT orderyear, COUNT(DISTINCT custid) AS numcusts FROM C1 GROUP BY orderyear ) SELECT orderyear, numcusts FROM C2 WHERE numcusts > 70; GO
Omong-omong, jika tidak jelas, CTE yang menjadi dasar kueri dalam tampilan dapat bersifat rekursif.
Mari kita lanjutkan ke kasus di mana Anda memerlukan beberapa referensi ke ekspresi tabel yang sama dari kueri luar. Tugas untuk contoh ini adalah menghitung jumlah pesanan tahunan per tahun, dan membandingkan jumlah di setiap tahun dengan tahun sebelumnya. Cara termudah untuk mencapai ini sebenarnya dengan menggunakan fungsi jendela LAG, tetapi kami akan menggunakan gabungan antara dua contoh ekspresi tabel yang mewakili jumlah pesanan tahunan hanya untuk membandingkan kasus multi-referensi di antara tiga alat.
Ini adalah kode yang kami gunakan sebelumnya dalam seri untuk menangani tugas dengan tabel turunan:
SELECT CUR.orderyear, CUR.numorders, CUR.numorders - PRV.numorders AS diff FROM ( SELECT YEAR(orderdate) AS orderyear, COUNT(*) AS numorders FROM Sales.Orders GROUP BY YEAR(orderdate) ) AS CUR LEFT OUTER JOIN ( SELECT YEAR(orderdate) AS orderyear, COUNT(*) AS numorders FROM Sales.Orders GROUP BY YEAR(orderdate) ) AS PRV ON CUR.orderyear = PRV.orderyear + 1;
Ada kelemahan yang sangat jelas di sini. Anda harus mengulang definisi ekspresi tabel dua kali. Anda pada dasarnya mendefinisikan dua ekspresi tabel bernama berdasarkan kode kueri yang sama.
Berikut kode yang menangani tugas yang sama menggunakan CTE:
WITH OrdCount AS ( SELECT YEAR(orderdate) AS orderyear, COUNT(*) AS numorders FROM Sales.Orders GROUP BY YEAR(orderdate) ) SELECT CUR.orderyear, CUR.numorders, CUR.numorders - PRV.numorders AS diff FROM OrdCount AS CUR LEFT OUTER JOIN OrdCount AS PRV ON CUR.orderyear = PRV.orderyear + 1;
Ada keuntungan yang jelas di sini; Anda hanya mendefinisikan satu ekspresi tabel bernama berdasarkan satu contoh kueri dalam, dan merujuknya dua kali dari kueri luar.
Tampilan lebih mirip dengan CTE dalam pengertian ini. Anda hanya menentukan satu tampilan berdasarkan hanya satu salinan kueri, seperti:
CREATE OR ALTER VIEW Sales.YearlyOrderCounts AS SELECT YEAR(orderdate) AS orderyear, COUNT(*) AS numorders FROM Sales.Orders GROUP BY YEAR(orderdate); GO
Tetapi lebih baik daripada dengan CTE, Anda tidak dibatasi untuk menggunakan kembali ekspresi tabel bernama hanya di pernyataan luar. Anda dapat menggunakan kembali nama tampilan beberapa kali yang Anda suka, dengan sejumlah kueri yang tidak terkait, selama Anda memiliki izin yang tepat. Berikut kode untuk mencapai tugas dengan menggunakan beberapa referensi ke tampilan:
SELECT CUR.orderyear, CUR.numorders, CUR.numorders - PRV.numorders AS diff FROM Sales.YearlyOrderCounts AS CUR LEFT OUTER JOIN Sales.YearlyOrderCounts AS PRV ON CUR.orderyear = PRV.orderyear + 1;
Sepertinya tampilan lebih mirip dengan CTE daripada tabel turunan, dengan fungsionalitas ekstra sebagai alat yang lebih dapat digunakan kembali, dengan kemampuan untuk mengontrol izin. Atau untuk membalikkannya, mungkin tepat untuk menganggap CTE sebagai pandangan dengan cakupan pernyataan. Sekarang yang benar-benar luar biasa adalah jika kita juga memiliki ekspresi tabel bernama dengan cakupan yang lebih luas daripada CTE, lebih sempit daripada tampilan. Misalnya, bukankah lebih bagus jika kita memiliki ekspresi tabel bernama cakupan tingkat sesi?
Ringkasan
Saya suka topik ini. Ada begitu banyak ekspresi tabel yang berakar pada teori relasional, yang pada gilirannya berakar pada matematika. Saya suka mengetahui apa istilah yang tepat untuk sesuatu, dan umumnya memastikan bahwa saya memiliki fondasi yang dipikirkan dengan hati-hati, bahkan jika bagi sebagian orang itu mungkin tampak pilih-pilih dan terlalu bertele-tele. Melihat kembali proses pembelajaran saya selama bertahun-tahun, saya dapat melihat jalur yang sangat jelas antara bersikeras pada pemahaman dasar yang baik, menggunakan terminologi yang benar, dan benar-benar mengetahui hal-hal Anda nanti ketika sampai pada hal-hal yang jauh lebih maju dan kompleks.
Jadi, apa bagian penting dalam hal penayangan?
- Tampilan adalah tabel.
- Ini adalah tabel yang diturunkan dari kueri (ekspresi tabel).
- Ini diberi nama yang bagi pengguna tampak seperti nama tabel, karena itu adalah nama tabel.
- Ini dibuat sebagai objek permanen dalam database.
- Anda dapat mengontrol izin akses terhadap tampilan.
Tampilan mirip dengan CTE dalam beberapa hal. Dalam arti bahwa Anda mengembangkan solusi Anda secara modular, dengan fokus pada satu unit pada satu waktu dari awal hingga akhir. Juga dalam arti bahwa Anda dapat memiliki banyak referensi ke nama tampilan dari kueri luar. Namun lebih baik daripada CTE, tampilan tidak terbatas hanya pada cakupan pernyataan luar, melainkan dapat digunakan kembali hingga dihapus dari database.
Ada banyak lagi yang bisa dikatakan tentang penayangan, dan saya akan melanjutkan diskusi bulan depan. Sementara itu, saya ingin meninggalkan Anda dengan pikiran. Dengan tabel turunan dan CTE, Anda dapat membuat kasus yang mendukung SELECT * dalam kueri dalam. Lihat kasus yang saya buat untuk itu di Bagian 3 dalam seri untuk detailnya. Bisakah Anda membuat kasus serupa dengan pandangan, atau apakah itu ide yang buruk dengan itu?