Di dua bagian sebelumnya, kami telah menyajikan model database langsung untuk bisnis berbasis langganan dan gudang data (DWH) yang dapat kami gunakan untuk pelaporan. Meskipun jelas bahwa mereka harus bekerja sama, tidak ada hubungan antara kedua model ini. Hari ini, kami akan mengambil langkah berikutnya dan menulis kode untuk mentransfer data dari database langsung ke DWH kami.
Model Data
Sebelum kita menyelami kodenya, mari kita ingatkan diri kita sendiri tentang dua model yang akan kita kerjakan. Pertama adalah model data transaksional yang akan kita gunakan untuk menyimpan data real-time kita. Mengingat bahwa kami menjalankan bisnis berbasis langganan, kami perlu menyimpan detail pelanggan dan langganan, pesanan pelanggan, dan status pesanan.
Ada banyak hal yang dapat kami tambahkan ke model ini, seperti melacak pembayaran dan menyimpan data historis (terutama perubahan data pelanggan dan langganan). Untuk menekankan proses ETL (extract, transform, and load), saya ingin membuat model ini sesederhana mungkin.
Menggunakan model data transaksional sebagai database pelaporan mungkin berhasil dalam beberapa kasus, tetapi tidak akan berhasil untuk semua kasus. Kami telah menyebutkannya, tetapi ada baiknya untuk mengulanginya. Jika kita ingin memisahkan tugas pelaporan kita dari proses waktu nyata, kita harus membuat semacam database pelaporan. Gudang data adalah salah satu solusinya.
DWH kami berpusat di sekitar empat tabel fakta. Dua yang pertama melacak jumlah pelanggan dan langganan pada tingkat harian. Dua sisanya melacak jumlah pengiriman dan produk yang termasuk dalam pengiriman ini.
Asumsi saya adalah bahwa kami akan menjalankan proses ETL kami sekali sehari. Pertama, kami akan mengisi tabel dimensi dengan nilai baru (jika diperlukan). Setelah itu, kita akan mengisi tabel fakta.
Untuk menghindari pengulangan yang tidak perlu, saya hanya akan mendemonstrasikan kode yang akan mengisi dua tabel dimensi pertama dan dua tabel fakta pertama. Tabel yang tersisa dapat diisi menggunakan kode yang sangat mirip. Saya mendorong Anda untuk menuliskan kodenya sendiri. Tidak ada cara yang lebih baik untuk mempelajari sesuatu yang baru selain dengan mencobanya.
Idenya:Tabel Dimensi
Ide umumnya adalah untuk membuat prosedur tersimpan yang dapat kita gunakan secara teratur untuk mengisi DWH -- tabel dimensi serta tabel fakta. Prosedur ini akan mentransfer data antara dua database di server yang sama. Ini berarti bahwa beberapa kueri di dalam prosedur ini akan menggunakan tabel dari kedua database. Ini diharapkan; kita perlu membandingkan status DWH dengan DB langsung dan membuat perubahan pada DWH sesuai dengan apa yang terjadi di DB langsung.
Kami memiliki empat tabel dimensi di DWH kami:dim_time
, dim_city
, dim_product
, dan dim_delivery_status
.
Dimensi waktu diisi dengan menambahkan tanggal sebelumnya. Asumsi utamanya adalah kami akan menjalankan prosedur ini setiap hari, setelah bisnis tutup.
Dimensi kota dan produk akan bergantung pada nilai saat ini yang disimpan di city
dan product
kamus dalam database langsung. Jika kita menambahkan sesuatu ke kamus ini, maka nilai baru akan ditambahkan ke tabel dimensi pada pembaruan DWH berikutnya.
Tabel dimensi terakhir adalah dim_delivery_status
meja. Itu tidak akan diperbarui karena hanya berisi tiga nilai default. Pengiriman sedang dalam perjalanan, dibatalkan, atau dikirim.
Ide:Tabel Fakta
Mengisi tabel fakta sebenarnya adalah pekerjaan nyata. Sementara kamus dalam database langsung tidak berisi atribut stempel waktu, tabel dengan data yang dimasukkan sebagai hasil dari operasi kami melakukannya. Anda akan melihat dua atribut stempel waktu, time_inserted
dan time_updated
, dalam model data.
Sekali lagi, saya berasumsi bahwa kami akan berhasil menjalankan impor DWH sekali sehari. Ini memungkinkan kami untuk menggabungkan data pada tingkat harian. Kami akan menghitung jumlah pelanggan dan langganan yang aktif dan yang dibatalkan, serta pengiriman dan produk yang dikirim untuk tanggal tersebut.
Model langsung kami berfungsi dengan baik jika kami menjalankan prosedur penyisipan setelah COB (tutup bisnis). Namun, jika kita menginginkan lebih banyak fleksibilitas, kita harus membuat beberapa perubahan pada modelnya. Salah satu perubahan tersebut dapat memiliki tabel riwayat terpisah untuk melacak saat yang tepat ketika data apa pun yang terkait dengan pelanggan atau langganan berubah. Dengan organisasi kami saat ini, kami akan tahu bahwa perubahan telah terjadi, tetapi kami tidak akan tahu apakah ada perubahan sebelum ini (misalnya pelanggan membatalkan kemarin, mengaktifkan kembali akunnya setelah tengah malam, dan kemudian membatalkan lagi hari ini) .
Mengisi Tabel Dimensi
Seperti yang disebutkan sebelumnya, saya akan menggunakan asumsi bahwa kami akan menjalankan impor DWH tepat sekali sehari. Jika bukan itu masalahnya, kita memerlukan kode tambahan untuk menghapus data yang baru dimasukkan dari tabel dimensi dan fakta. Untuk tabel dimensi, ini akan dibatasi untuk menghapus tanggal tertentu.
Pertama, kami akan memeriksa apakah tanggal yang diberikan ada di dim_time
meja. Jika tidak, kami akan menambahkan baris baru ke tabel; jika ya, kita tidak perlu melakukan apa pun. Dalam kebanyakan kasus, semua tanggal dimasukkan selama penyebaran produksi awal. Tapi saya akan menggunakan contoh ini untuk tujuan pendidikan.
Untuk dim_city
dan dim_product
dimensi, saya hanya akan menambahkan nilai baru yang saya deteksi di city
dan product
tabel. Saya tidak akan melakukan penghapusan karena nilai yang dimasukkan sebelumnya dapat direferensikan di beberapa tabel fakta. Kita bisa pergi dengan penghapusan lunak, mis. memiliki tanda “aktif” yang dapat kita nyalakan dan matikan.
Untuk tabel terakhir, dim_delivery_status
, saya tidak akan melakukan apa pun karena akan selalu berisi tiga nilai yang sama.
Kode di bawah ini membuat prosedur yang akan mengisi tabel dimensi dim_time
dan dim_city
.
Untuk dimensi waktu, saya akan menambahkan tanggal kemarin. Saya akan dengan asumsi bahwa proses ETL dimulai tepat setelah tengah malam. Saya akan memeriksa apakah dimensi itu sudah ada dan jika tidak, saya akan menambahkan tanggal baru di tabel.
Untuk dimensi kota, saya akan menggunakan LEFT JOIN untuk menggabungkan data dari database langsung dan database DWH untuk menentukan baris mana yang hilang. Kemudian saya hanya akan menambahkan data yang hilang ke tabel dimensi. Perlu disebutkan bahwa ada beberapa cara untuk memeriksa apakah data telah diubah. Proses ini disebut change data capture, atau CDC. Metode umum adalah memeriksa stempel waktu atau versi yang diperbarui. Ada beberapa cara tambahan, tetapi di luar cakupan artikel ini.
Mari kita lihat kodenya sekarang, yang ditulis menggunakan sintaks MySQL .
DROP PROCEDURE IF EXISTS p_update_dimensions// CREATE PROCEDURE p_update_dimensions () BEGIN SET @time_exists = 0; SET @time_date = DATE_ADD(DATE(NOW()), INTERVAL -1 DAY); -- procedure populates dimension tables with new values -- dim_time SET @time_exists = (SELECT COUNT(*) FROM subscription_dwh.dim_time dim_time WHERE dim_time.time_date = @time_date); IF (@time_exists = 0) THEN INSERT INTO subscription_dwh.`dim_time`(`time_date`, `time_year`, `time_month`, `time_week`, `time_weekday`, `ts`) SELECT @time_date AS time_date, YEAR(@time_date) AS time_year, MONTH(@time_date) AS time_month, WEEK(@time_date) AS time_week, WEEKDAY(@time_date) AS time_weekday, NOW() AS ts; END IF; -- dim_city INSERT INTO subscription_dwh.`dim_city`(`city_name`, `postal_code`, `country_name`, `ts`) SELECT city_live.city_name, city_live.postal_code, country_live.country_name, Now() FROM subscription_live.city city_live INNER JOIN subscription_live.country country_live ON city_live.country_id = country_live.id LEFT JOIN subscription_dwh.dim_city city_dwh ON city_live.city_name = city_dwh.city_name AND city_live.postal_code = city_dwh.postal_code AND country_live.country_name = city_dwh.country_name WHERE city_dwh.id IS NULL; END// -- CALL p_update_dimensions ()
Menjalankan prosedur ini -- yang kami lakukan menggunakan prosedur yang dikomentari CALL -- menyisipkan tanggal baru dan semua kota yang hilang ke dalam tabel dimensi. Coba tambahkan kode Anda sendiri untuk mengisi tabel dua dimensi yang tersisa dengan nilai baru.
Proses ETL di Data Warehouse
Gagasan utama di balik pergudangan data adalah untuk memuat data agregat dalam format yang diinginkan. Tentu saja, kita harus mengetahui format itu bahkan sebelum kita mulai membangun gudang. Jika kita telah melakukan semuanya sesuai rencana, kita bisa mendapatkan semua manfaat yang ditawarkan DWH kepada kita. Manfaat utama adalah peningkatan kinerja saat menjalankan kueri. Kueri kami bekerja dengan catatan yang lebih sedikit (karena dikumpulkan) dan dijalankan di database pelaporan (bukan yang langsung).
Tapi sebelum kita bisa query, kita perlu menyimpan fakta di database kita. Cara kami melakukannya tergantung pada apa yang perlu kami lakukan dengan data kami nanti. Jika kita tidak memiliki gambaran keseluruhan yang baik sebelum mulai membangun DWH kita, kita bisa segera menemukan diri kita dalam masalah! segera.
Nama proses ini adalah ETL:E =Extract, T =Transform, L =Load. Ini mengambil data, mengubahnya agar sesuai dengan struktur DWH, dan memuatnya di DWH. Tepatnya, proses sebenarnya yang akan kita gunakan adalah ELT:Extract, Load, Transform. Karena kami menggunakan prosedur tersimpan, kami akan mengekstrak data, memuatnya, dan kemudian mengubahnya untuk memenuhi kebutuhan kami. Senang mengetahui bahwa meskipun ETL dan ELT sedikit berbeda, istilah tersebut terkadang digunakan secara bergantian.
Mengisi Tabel Fakta
Mengisi tabel fakta adalah alasan kami benar-benar ada di sini. Hari ini, saya akan mengisi dua tabel fakta, fact_customer_subscribed
tabel dan fact_subscription_status
meja. Dua tabel fakta yang tersisa adalah milik Anda untuk dicoba sebagai pekerjaan rumah.
Sebelum kita beralih ke pengisian tabel fakta, kita harus mengasumsikan bahwa tabel dimensi diisi dengan nilai baru. Mengisi tabel fakta mengikuti pola yang sama. Karena mereka memiliki struktur yang sama, saya akan menjelaskan keduanya bersama-sama.
Kami mengelompokkan data menurut dua dimensi:waktu dan kota. Dimensi waktu akan disetel ke kemarin, dan kami akan menemukan ID catatan terkait di dim_time
tabel dengan membandingkan tanggal (INNER JOIN terakhir di kedua kueri).
ID dim_city
diekstraksi dengan menggabungkan semua atribut yang membentuk kombinasi UNIK dalam tabel dimensi (nama kota, kode pos, dan nama negara).
Dalam kueri ini, kami akan menguji nilai dengan CASE dan kemudian SUM. Untuk pelanggan aktif dan tidak aktif, saya belum menguji tanggalnya. Namun, saya telah memilih nilai apa adanya untuk bidang ini. Untuk akun baru dan yang dibatalkan, saya telah menguji waktu yang diperbarui.
DROP PROCEDURE IF EXISTS p_update_facts// CREATE PROCEDURE p_update_facts () BEGIN SET @time_date = DATE_ADD(DATE(NOW()), INTERVAL -1 DAY); -- procedure populates fact tables with new values -- fact_customer_subscribed INSERT INTO `fact_customer_subscribed`(`dim_city_id`, `dim_time_id`, `total_active`, `total_inactive`, `daily_new`, `daily_canceled`, `ts`) SELECT city_dwh.id AS dim_ctiy_id, time_dwh.id AS dim_time_id, SUM(CASE WHEN customer_live.active = 1 THEN 1 ELSE 0 END) AS total_active, SUM(CASE WHEN customer_live.active = 0 THEN 1 ELSE 0 END) AS total_inactive, SUM(CASE WHEN customer_live.active = 1 AND DATE(customer_live.time_updated) = @time_date THEN 1 ELSE 0 END) AS daily_new, SUM(CASE WHEN customer_live.active = 0 AND DATE(customer_live.time_updated) = @time_date THEN 1 ELSE 0 END) AS daily_canceled, MIN(NOW()) AS ts FROM subscription_live.`customer` customer_live INNER JOIN subscription_live.`city` city_live ON customer_live.city_id = city_live.id INNER JOIN subscription_live.`country` country_live ON city_live.country_id = country_live.id INNER JOIN subscription_dwh.dim_city city_dwh ON city_live.city_name = city_dwh.city_name AND city_live.postal_code = city_dwh.postal_code AND country_live.country_name = city_dwh.country_name INNER JOIN subscription_dwh.dim_time time_dwh ON time_dwh.time_date = @time_date GROUP BY city_dwh.id, time_dwh.id; -- fact_subscription_status INSERT INTO `fact_subscription_status`(`dim_city_id`, `dim_time_id`, `total_active`, `total_inactive`, `daily_new`, `daily_canceled`, `ts`) SELECT city_dwh.id AS dim_ctiy_id, time_dwh.id AS dim_time_id, SUM(CASE WHEN subscription_live.active = 1 THEN 1 ELSE 0 END) AS total_active, SUM(CASE WHEN subscription_live.active = 0 THEN 1 ELSE 0 END) AS total_inactive, SUM(CASE WHEN subscription_live.active = 1 AND DATE(subscription_live.time_updated) = @time_date THEN 1 ELSE 0 END) AS daily_new, SUM(CASE WHEN subscription_live.active = 0 AND DATE(subscription_live.time_updated) = @time_date THEN 1 ELSE 0 END) AS daily_canceled, MIN(NOW()) AS ts FROM subscription_live.`customer` customer_live INNER JOIN subscription_live.`subscription` subscription_live ON subscription_live.customer_id = customer_live.id INNER JOIN subscription_live.`city` city_live ON customer_live.city_id = city_live.id INNER JOIN subscription_live.`country` country_live ON city_live.country_id = country_live.id INNER JOIN subscription_dwh.dim_city city_dwh ON city_live.city_name = city_dwh.city_name AND city_live.postal_code = city_dwh.postal_code AND country_live.country_name = city_dwh.country_name INNER JOIN subscription_dwh.dim_time time_dwh ON time_dwh.time_date = @time_date GROUP BY city_dwh.id, time_dwh.id; END// -- CALL p_update_facts ()
Sekali lagi, saya telah mengomentari baris terakhir. Hapus komentar dan Anda dapat menggunakan baris ini untuk memanggil prosedur dan memasukkan nilai baru. Harap perhatikan bahwa saya belum menghapus nilai lama yang ada, jadi prosedur ini tidak akan berfungsi jika kita sudah memiliki nilai untuk tanggal dan kota tersebut. Ini dapat diselesaikan dengan melakukan penghapusan sebelum penyisipan.
Ingat, kita perlu mengisi tabel fakta yang tersisa di DWH kita. Saya mendorong Anda untuk mencobanya sendiri!
Hal lain yang saya pasti akan merekomendasikan adalah menempatkan seluruh proses di dalam transaksi. Itu akan memastikan bahwa semua penyisipan berhasil atau tidak ada yang dibuat. Ini sangat penting ketika kita ingin menghindari sebagian data dimasukkan, mis. jika kita memiliki beberapa prosedur untuk menyisipkan dimensi dan fakta dan beberapa di antaranya melakukan tugasnya sementara yang lain gagal.
Bagaimana Menurut Anda?
Hari ini kita telah melihat bagaimana kita dapat melakukan proses ELT/ETL dan memuat data dari database langsung ke gudang data. Meskipun proses yang kami tunjukkan cukup sederhana, proses ini berisi semua elemen yang diperlukan untuk E(mengekstrak) data, T(mengubahnya) ke dalam format yang sesuai, dan akhirnya L(mengisi) ke dalam DWH. Bagaimana menurutmu? Beri tahu kami pengalaman Anda di komentar di bawah.