SQL Server: Tetapkan tanggal di server SQL, tetapi tetap deterministik

(Ini terkait dengan Menetapkan tanggal di server SQL.)

Apakah ada ekspresi deterministik untuk menentukan DATETIME? Ketika saya menggunakan ini sebagai rumus kolom yang dihitung:

DATEADD(dd, DATEDIFF(dd, 0, [datetime_column]), 0)

saya mendapatkan kesalahan ketika saya menempatkan indeks pada kolom itu:

Tidak dapat membuat indeks karena kolom kunci 'Tanggal Efektif' tidak bersifat deterministik atau tidak tepat.

Namun DATEDIFF dan DATEADD keduanya merupakan fungsi deterministik menurut definisi. Dimana hasil tangkapannya? Apa itu mungkin?


person Tomalak    schedule 21.11.2008    source sumber


Jawaban (6)


Dugaan saya, ini semacam bug. Di SQL 2005 saya dapat membuat tampilan yang diindeks tanpa masalah (kode ada di bawah). Ketika saya mencoba menjalankannya di SQL 2000, saya mendapatkan kesalahan yang sama seperti yang Anda dapatkan.

Berikut ini tampaknya berfungsi pada SQL 2000, tetapi saya mendapat peringatan bahwa indeks akan diabaikan DAN Anda harus mengonversi setiap kali Anda memilih dari tampilan.

CONVERT(CHAR(8), datetime_column, 112)

Bekerja di SQL 2005:

CREATE TABLE dbo.Test_Determinism (
    datetime_column DATETIME    NOT NULL    DEFAULT GETDATE())
GO

CREATE VIEW dbo.Test_Determinism_View
WITH SCHEMABINDING
AS
    SELECT
        DATEADD(dd, DATEDIFF(dd, 0, [datetime_column]), 0) AS EffectiveDate
    FROM
        dbo.Test_Determinism
GO

CREATE UNIQUE CLUSTERED INDEX IDX_Test_Determinism_View ON dbo.Test_Determinism_View (EffectiveDate)
GO
person Tom H    schedule 21.11.2008

Apakah kolom [datetime_column] Anda memiliki nilai default yang disetel ke "getDate()" ??

Jika demikian, karena fungsi getdate() non-deterministik, ini akan menyebabkan kesalahan ini...

Apakah fungsi yang ditentukan pengguna bersifat deterministik atau nondeterministik bergantung pada cara fungsi tersebut dikodekan. Fungsi yang ditentukan pengguna bersifat deterministik jika:

  1. Fungsinya terikat skema.
  2. Semua fungsi bawaan atau yang ditentukan pengguna yang dipanggil oleh fungsi yang ditentukan pengguna bersifat deterministik.
  3. Badan fungsi tidak mereferensikan objek database di luar cakupan fungsi. Misalnya, fungsi deterministik tidak bisa mereferensikan tabel selain variabel tabel yang bersifat lokal pada fungsi tersebut.
  4. Fungsi ini tidak memanggil prosedur tersimpan yang diperluas.

Fungsi buatan pengguna yang tidak memenuhi kriteria ini ditandai sebagai nondeterministik. Fungsi nondeterministik bawaan tidak diperbolehkan dalam isi fungsi yang ditentukan pengguna.

person Charles Bretana    schedule 21.11.2008
comment
getDate bersifat non-deterministik, saya yakin itulah alasannya - person kristof; 21.11.2008
comment
Namun ini hanyalah nilai default. Itu bukan bagian dari rumus. BTW saya mendapatkan kesalahan yang sama untuk kolom NULL default. - person Tomalak; 21.11.2008
comment
Saya kira subrutin yang melakukan ini tidak membedakan antara bagian definisi kolom yang mewakili rumus, dan bagian yang mewakili nilai default... - person Charles Bretana; 21.11.2008
comment
Ya itu. Saya sudah mencoba saran Kristof. - person Tomalak; 21.11.2008

Coba ini:

CAST(FLOOR(CAST([datetime_column] as FLOAT)) AS DateTime)

Ini seharusnya berjalan lebih cepat daripada opsi CONVERT.

person Joel Coehoorn    schedule 21.11.2008
comment
CAST bersifat non-deterministik untuk nilai datetime. - person Tomalak; 21.11.2008

Inilah jawaban terbaik saya untuk menjawab pertanyaan awal:

Coba ini:

/* create a deterministic schema bound function */
CREATE FUNCTION FloorDate(@dt datetime)
RETURNS datetime
WITH SCHEMABINDING
AS
BEGIN 
    RETURN CONVERT(datetime,  FLOOR(CONVERT(float, @dt)))
END
GO

Untuk mengujinya, coba yang berikut ini. Harap perhatikan penggunaan "PERSISTED" untuk kolom terhitung dan penggunaan [dbo.] ketika mengacu pada fungsi

/*create a test table */
CREATE TABLE [dbo].[TableTestFloorDate](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [TestDate] [datetime] NOT NULL,
    [TestFloorDate]  AS ([dbo].[FloorDate]([TestDate])) PERSISTED,
 CONSTRAINT [PK_TableTestFloorDate] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
) 

Anda sekarang seharusnya dapat menambahkan indeks pada kolom yang dihitung (tetapi lihat gotcha nanti)

CREATE INDEX IX_TestFloorDate ON  [dbo].[TableTestFloorDate](TestFloorDate)

Masukkan beberapa data acak sebanyak yang Anda inginkan tetapi lebih banyak (1000+) lebih baik jika Anda ingin menguji rencana penggunaan/eksekusi indeks

INSERT INTO TableTestFloorDate (TestDate) VALUES( convert(datetime, RAND()*50000))

Dapatkan hasilnya

SELECT * FROM TableTestFloorDate WHERE TestFloorDate='2013-2-2'

Sekarang inilah GOTCHA... Indeks yang telah dibuat pada kolom terhitung tidak digunakan! Sebaliknya, bahkan ketika memilih data pada bidang TestFloorDate yang bertahan, SQLServer (atau setidaknya versi saya) lebih memilih indeks pada TestDate.

CREATE INDEX IX_TestFloorDate ON  [dbo].[TableTestFloorDate](TestDate)

Saya cukup yakin (dari ingatan) bahwa indeks pada kolom yang dihitung dan dipertahankan bermanfaat dari perspektif kinerja - saya kira Anda hanya perlu mencoba/menguji untuk penggunaan spesifik Anda sendiri

(Semoga saya telah membantu!)

person dunxz    schedule 24.07.2013
comment
Saya tidak punya cara untuk mengujinya sekarang. Apakah ini kompatibel dengan SQL Server 2000 (saya tahu ini kuno, tetapi tag dalam pertanyaan menunjukkan versi ini)? - person Tomalak; 24.07.2013
comment
Hal di atas telah diuji di Sql2012 tetapi dari ingatan saya tidak mengerti mengapa itu tidak berfungsi di Sql2k dengan cara yang persis seperti dijelaskan di atas - person dunxz; 25.07.2013

Saya menyarankan yang lebih sederhana:

 cast(cast([datetime_column] as int) as datetime)

tapi saya curiga Anda akan mengalami masalah yang sama.

Sekarang jika masalahnya adalah mengembalikan ke waktu tertentu, Anda mungkin ingin mempertimbangkan untuk menggunakan cast([datetime_column] as int) saja sebagai bidang terpisah, hanya untuk indeks.

person James Curran    schedule 21.11.2008
comment
Masalahnya juga ada pada casting from datetime (atau convert()ing, dalam hal ini). - person Tomalak; 21.11.2008

Lihat pertanyaan yang diajukan dan dijawab oleh Cade Roux. Mungkin solusinya adalah dengan membuat fungsi menggunakan WITH SCHEMABINDING dan kemudian menggunakannya di kolom terhitung

EDIT

Saya memahami bahwa tujuan Anda adalah untuk dapat memiliki indeks pada kolom itu.

Jika hal itu tidak dapat dilakukan dengan kolom terhitung maka mungkin satu-satunya pilihan adalah membuat kolom biasa dan mengubah data di kolom tersebut setiap kali Anda memperbarui kolom yang menjadi dasarnya. (katakan pada trigger)

person kristof    schedule 21.11.2008