Saya percaya bahwa ini memang cacat desain, meskipun yang tidak spesifik untuk SQL Server 2016, karena semua implementasi yang ada dari tabel temporal lainnya (sejauh yang saya tahu) memiliki kelemahan yang sama. Masalah yang dapat timbul dengan tabel temporal karena ini cukup parah; skenario dalam contoh Anda ringan dibandingkan dengan apa yang bisa salah secara umum:
Referensi kunci asing yang rusak : Misalkan kita memiliki dua tabel temporal, dengan tabel A memiliki referensi kunci asing ke tabel B. Sekarang katakanlah kita memiliki dua transaksi, keduanya berjalan pada tingkat isolasi BACA KOMITMEN: transaksi 1 dimulai sebelum transaksi 2, transaksi 2 menyisipkan baris ke dalam tabel B dan melakukan, lalu transaksi 1 menyisipkan baris di tabel A dengan referensi ke baris yang baru ditambahkan dari B. Karena penambahan baris baru ke B sudah dilakukan, batasan kunci asing puas dan transaksi 1 berhasil melakukan komitmen. Namun, jika kita melihat database "SEBAGAIMANA ADANYA" di beberapa waktu antara ketika transaksi 1 dimulai dan ketika transaksi 2 dimulai, maka kita akan melihat tabel A dengan referensi ke deretan B yang tidak ada. Jadi dalam hal ini,tabel temporal memberikan pandangan yang tidak konsisten dari database . Ini tentu saja bukan maksud dari standar SQL: 2011, yang menyatakan,
Baris sistem historis dalam tabel versi sistem membentuk snapshot tidak berubah dari masa lalu. Setiap kendala yang berlaku ketika baris sistem historis dibuat akan sudah diperiksa ketika baris itu adalah baris sistem saat ini, jadi tidak pernah ada kebutuhan untuk menegakkan batasan pada baris sistem historis.
Kunci primer non-unik : Misalkan kita memiliki tabel dengan kunci utama dan dua transaksi, keduanya pada tingkat isolasi BACA KOMITMEN, di mana hal berikut terjadi: Setelah transaksi 1 dimulai tetapi sebelum menyentuh tabel ini, transaksi 2 menghapus tertentu baris meja dan melakukan. Kemudian, transaksi 1 menyisipkan baris baru dengan kunci utama yang sama dengan yang telah dihapus. Ini berjalan dengan baik, tetapi ketika Anda melihat tabel SEBAGAI waktu di antara ketika transaksi 1 dimulai dan ketika transaksi 2 dimulai, kita akan melihat dua baris dengan kunci utama yang sama.
Kesalahan pada pembaruan bersamaan : Katakanlah kita memiliki tabel dan dua transaksi yang keduanya memperbarui baris yang sama di dalamnya, sekali lagi pada tingkat isolasi BACA KOMITMEN. Transaksi 1 dimulai terlebih dahulu, tetapi transaksi 2 adalah yang pertama untuk memperbarui baris. Transaksi 2 kemudian melakukan, dan transaksi 1 kemudian melakukan pembaruan yang berbeda pada baris dan melakukan. Ini semua baik-baik saja, kecuali bahwa jika ini adalah tabel temporal, setelah pelaksanaan pembaruan dalam transaksi 1 ketika sistem pergi untuk memasukkan baris yang diperlukan ke dalam tabel riwayat, SysStartTime yang dihasilkan akan menjadi waktu mulai transaksi 2, sedangkan SysEndTime akan menjadi waktu mulai transaksi 1, yang bukan interval waktu yang valid karena SysEndTime akan sebelum SysStartTime. Dalam hal ini SQL Server melempar kesalahan dan memutar kembali transaksi (misalnya, lihatdiskusi ini ). Ini sangat tidak menyenangkan, karena pada tingkat isolasi READ COMMITTED, tidak diharapkan bahwa masalah konkurensi akan menyebabkan kegagalan langsung, yang berarti bahwa aplikasi tidak perlu dipersiapkan untuk melakukan upaya coba lagi. Secara khusus, ini bertentangan dengan "jaminan" dalam dokumentasi Microsoft:
Perilaku ini menjamin bahwa aplikasi lawas Anda akan terus berfungsi ketika Anda mengaktifkan versi sistem pada tabel yang akan mendapat manfaat dari versi. ( tautan )
Implementasi lain dari tabel temporal telah berurusan dengan skenario ini (dua transaksi bersamaan memperbarui baris yang sama) dengan menawarkan opsi untuk secara otomatis "menyesuaikan" cap waktu jika mereka tidak valid (lihat di sini dan di sini ). Ini adalah solusi yang buruk, karena memiliki konsekuensi yang disayangkan dari mematahkan atomicity transaksi, karena pernyataan lain dalam transaksi yang sama umumnya tidak akan memiliki stempel waktu mereka disesuaikan dengan cara yang sama; yaitu, dengan solusi ini, jika kita melihat basis data "SEBAGAIMANA Waktu" tertentu maka kita dapat melihat transaksi yang sebagian dieksekusi.
Larutan: Anda sudah menyarankan solusi yang jelas, yaitu agar implementasi menggunakan waktu akhir transaksi (yaitu waktu komit) alih-alih waktu mulai. Ya memang benar bahwa ketika kita mengeksekusi pernyataan di tengah-tengah transaksi, tidak mungkin untuk mengetahui berapa waktu komit akan terjadi (seperti di masa depan, atau bahkan mungkin tidak ada jika transaksi akan digulirkan kembali). Tetapi ini tidak berarti solusinya tidak dapat diterapkan; itu hanya harus dilakukan dengan cara yang berbeda. Misalnya, ketika melakukan pernyataan UPDATE atau DELETE, dalam membuat baris sejarah sistem hanya bisa memasukkan ID transaksi saat ini daripada waktu mulai, dan kemudian ID dapat dikonversi ke stempel waktu kemudian oleh sistem setelah transaksi dilakukan. .
Dalam konteks implementasi semacam ini, saya akan menyarankan bahwa sebelum transaksi dilakukan, setiap baris yang ditambahkan ke tabel riwayat tidak boleh terlihat oleh pengguna. Dari perspektif pengguna, seharusnya hanya muncul bahwa baris ini ditambahkan (dengan stempel waktu komit) pada saat komit. Khususnya, jika transaksi tidak pernah berhasil dilakukan maka itu tidak akan pernah muncul dalam sejarah. Tentu saja, ini tidak konsisten dengan standar SQL: 2011 yang menggambarkan penyisipan ke riwayat (termasuk cap waktu) sebagai yang terjadi pada saat pernyataan UPDATE dan DELETE (sebagai lawan dari waktu komit). Tetapi saya tidak berpikir ini benar-benar penting, mengingat bahwa standar tidak pernah diimplementasikan dengan baik (dan bisa dibilang tidak pernah bisa) karena masalah yang dijelaskan di atas,
Dari sudut pandang kinerja, mungkin tampak tidak diinginkan bagi sistem untuk kembali dan meninjau kembali baris sejarah untuk mengisi stempel waktu komit. Tetapi tergantung pada bagaimana hal ini dilakukan, biayanya bisa sangat rendah. Saya tidak begitu akrab dengan bagaimana SQL Server bekerja secara internal, tetapi PostgreSQL misalnya menggunakan write-ahead-log, yang membuatnya sehingga jika beberapa pembaruan dilakukan pada bagian tabel yang sama, pembaruan tersebut dikonsolidasikan sehingga data hanya perlu ditulis satu kali ke halaman tabel fisik - dan itu biasanya berlaku dalam skenario ini. Bagaimanapun,
Tentu saja, karena (sejauh yang saya tahu) sistem semacam ini belum pernah diterapkan, saya tidak dapat mengatakan dengan pasti bahwa itu akan berhasil - mungkin ada sesuatu yang saya lewatkan - tetapi saya tidak melihat alasan apa pun mengapa itu tidak berhasil?
20160707 11:04:58
dan sekarang Anda memperbarui semua baris dengan stempel waktu itu. Tetapi pembaruan ini juga berjalan selama beberapa detik dan selesai pada20160707 11:05:02
, sekarang, stempel waktu mana yang merupakan akhir transaksi yang benar? Atau menganggap Anda digunakanRead Uncommited
di20160707 11:05:00
, dan mendapat baris yang dikembalikan, tetapi kemudianAS OF
tidak menunjukkan mereka.