Berikut adalah model untuk mencapai persyaratan yang Anda nyatakan.
Tautan ke Model Data Deret Waktu
Tautan ke Notasi IDEF1X bagi mereka yang tidak terbiasa dengan Standar Pemodelan Relasional.
-
Dinormalisasi ke 5NF; tidak ada kolom duplikat; tidak ada Anomali Pembaruan, tidak ada Nulls.
-
Saat Status Produk berubah, cukup masukkan baris ke dalam ProductStatus, dengan DateTime saat ini. Tidak perlu menyentuh baris sebelumnya (yang benar, dan tetap benar). Tidak ada nilai dummy yang harus ditafsirkan oleh alat laporan (selain aplikasi Anda).
-
DateTime adalah DateTime aktual saat Produk ditempatkan dalam Status itu; "Dari", jika Anda mau. "Kepada" mudah diturunkan:ini adalah DateTime dari baris (DateTime> "From") berikutnya untuk Produk; jika tidak ada, nilainya adalah DateTime saat ini (gunakan ISNULL).
Model pertama selesai; (ProductId, DateTime) cukup memberikan keunikan, untuk Primary Key. Namun, karena Anda meminta kecepatan untuk kondisi kueri tertentu, kami dapat menyempurnakan model di tingkat fisik, dan menyediakan:
-
Indeks (kami sudah memiliki Indeks PK, jadi kami akan meningkatkannya terlebih dahulu, sebelum menambahkan indeks kedua) untuk mendukung kueri tercakup (yang didasarkan pada pengaturan { ProductId | DateTime | Status } dapat diberikan oleh Indeks, tanpa harus untuk pergi ke baris data). Yang mengubah hubungan Status::ProductStatus dari Non-Identifying (garis putus-putus) menjadi Mengidentifikasi jenis (garis utuh).
-
Susunan PK dipilih berdasarkan bahwa sebagian besar kueri akan berupa Time Series, berdasarkan Product⇢DateTime⇢Status.
-
Indeks kedua disediakan untuk meningkatkan kecepatan kueri berdasarkan Status.
-
Dalam Alternate Arrangement, itu terbalik; yaitu, kami sebagian besar menginginkan status semua Produk saat ini.
-
Dalam semua versi ProductStatus, kolom DateTime di Indeks sekunder (bukan PK) adalah DESCending; yang terbaru adalah yang pertama.
Saya telah menyediakan diskusi yang Anda minta. Tentu saja, Anda perlu bereksperimen dengan kumpulan data dengan ukuran yang masuk akal, dan membuat keputusan sendiri. Jika ada sesuatu di sini yang Anda tidak mengerti, silakan bertanya, dan saya akan memperluas.
Respons terhadap Komentar
Laporkan semua Produk dengan Status Saat Ini 2
SELECT ProductId,
Description
FROM Product p,
ProductStatus ps
WHERE p.ProductId = ps.ProductId -- Join
AND StatusCode = 2 -- Request
AND DateTime = ( -- Current Status on the left ...
SELECT MAX(DateTime) -- Current Status row for outer Product
FROM ProductStatus ps_inner
WHERE p.ProductId = ps_inner.ProductId
)
-
ProductId
Diindeks, kolom terdepan, kedua sisi -
DateTime
di Terindeks, kolom ke-2 di Opsi Kueri Tercakup -
StatusCode
Diindeks, kolom ke-3 dalam Opsi Kueri Tercakup -
Sejak
StatusCode
dalam Indeks adalah DESCending, hanya satu pengambilan yang diperlukan untuk memenuhi kueri dalam -
baris diperlukan pada saat yang sama, untuk satu kueri; mereka berdekatan (karena Clstered Index); hampir selalu pada halaman yang sama karena ukuran baris yang pendek.
Ini adalah SQL biasa, sebuah subquery, menggunakan kekuatan mesin SQL, pemrosesan himpunan relasional. Ini adalah satu metode yang benar , tidak ada yang lebih cepat, dan metode lain apa pun akan lebih lambat. Alat laporan apa pun akan menghasilkan kode ini dengan beberapa klik, tanpa mengetik.
Dua Tanggal di ProductStatus
Kolom seperti DateTimeFrom dan DateTimeTo adalah kesalahan besar. Mari kita ambil urutan kepentingannya.
-
Ini adalah kesalahan Normalisasi kotor. "DateTimeTo" dengan mudah diturunkan dari DateTime tunggal dari baris berikutnya; karena itu berlebihan, kolom duplikat.
- Ketepatan tidak masuk ke dalamnya:yang mudah diselesaikan berdasarkan DataType (DATE, DATETIME, SMALLDATETIME). Apakah Anda menampilkan satu detik lebih sedikit, mikrodetik, atau nanodetik, adalah keputusan bisnis; itu tidak ada hubungannya dengan data yang disimpan.
-
Menerapkan kolom DateTo adalah duplikat 100% (dari DateTime dari baris berikutnya). Ini membutuhkan dua kali ruang disk . Untuk meja besar, itu akan menjadi pemborosan yang tidak perlu.
-
Mengingat ini adalah baris yang pendek, Anda memerlukan I/Os logis dan fisik dua kali lebih banyak untuk membaca tabel, pada setiap akses.
-
Dan ruang cache dua kali lebih banyak (atau dengan kata lain, hanya setengah dari jumlah baris yang akan masuk ke dalam ruang cache yang diberikan).
-
Dengan memperkenalkan kolom duplikat, Anda telah memperkenalkan kemungkinan kesalahan (nilai sekarang dapat diturunkan dua cara:dari kolom DateTimeTo duplikat atau DateTimeFrom dari baris berikutnya).
-
Ini juga merupakan Anomali Pembaruan . Saat Anda memperbarui DateTimeFrom apa pun Diperbarui, DateTimeTo dari baris sebelumnya harus diambil (bukan masalah besar karena sudah dekat) dan Diperbarui (masalah besar karena ini adalah kata kerja tambahan yang dapat dihindari).
-
"Lebih pendek" dan "pintasan pengkodean" tidak relevan, SQL adalah bahasa manipulasi data yang rumit, tetapi hanya SQL yang kita miliki (Hanya Menghadapinya). Siapa pun yang tidak dapat membuat kode subquery seharusnya tidak melakukan pengkodean. Siapa pun yang menggandakan kolom untuk memudahkan "kesulitan" pengkodean kecil seharusnya tidak menjadi model database.
Perhatikan dengan baik, bahwa jika aturan orde tertinggi (Normalisasi) dipertahankan, seluruh rangkaian masalah orde bawah dihilangkan.
Pikirkan dalam Kerangka Himpunan
-
Siapapun yang mengalami "kesulitan" atau mengalami "sakit" saat menulis SQL sederhana lumpuh dalam menjalankan fungsi pekerjaannya. Biasanya pengembang tidak berpikir dalam kerangka set dan Basis Data Relasional adalah model berorientasi himpunan .
-
Untuk kueri di atas, kita membutuhkan Current DateTime; karena ProductStatus adalah set Status Produk dalam urutan kronologis, kita hanya perlu yang terbaru, atau MAX(DateTime) dari set milik Produk.
-
Sekarang mari kita lihat sesuatu yang diduga "sulit", dalam hal set . Untuk laporan durasi setiap Produk berada dalam Status tertentu:DateTimeFrom adalah kolom yang tersedia, dan menentukan batas horizontal, sub set (kita dapat mengecualikan baris sebelumnya); DateTimeTo adalah yang paling awal dari sub set Status Produk.
SELECT ProductId,
Description,
[DateFrom] = DateTime,
[DateTo] = (
SELECT MIN(DateTime) -- earliest in subset
FROM ProductStatus ps_inner
WHERE p.ProductId = ps_inner.ProductId -- our Product
AND ps_inner.DateTime > ps.DateTime -- defines subset, cutoff
)
FROM Product p,
ProductStatus ps
WHERE p.ProductId = ps.ProductId
AND StatusCode = 2 -- Request
-
Berpikir dalam hal mendapatkan baris berikutnya berorientasi pada baris, tidak pemrosesan berorientasi set. Melumpuhkan, saat bekerja dengan database berorientasi kumpulan. Biarkan Pengoptimal melakukan semua pemikiran itu untuk Anda. Periksa SHOWPLAN Anda, ini mengoptimalkan dengan indah.
-
Ketidakmampuan untuk berpikir dalam set , sehingga dibatasi hanya untuk menulis kueri tingkat tunggal, bukanlah pembenaran yang masuk akal untuk:menerapkan duplikasi besar-besaran dan Pembaruan Anomali dalam database; membuang-buang sumber daya online dan ruang disk; menjamin setengah kinerja. Jauh lebih murah untuk mempelajari cara menulis subkueri SQL sederhana untuk mendapatkan data yang diperoleh dengan mudah.