Kapan kolom dihitung dihitung?


29

Kapan nilai untuk kolom yang dihitung ditentukan?

  • Kapan nilainya diambil?
  • Kapan nilainya berubah?
  • Lain waktu?

Saya menduga ini adalah pertanyaan pemula karena saya tidak menemukan apa pun dalam pencarian saya.

Jawaban:


19

Itu tergantung pada bagaimana Anda mendefinisikan kolom yang dihitung. Sebuah PERSISTEDkolom dihitung akan dihitung dan kemudian disimpan sebagai data dalam tabel. Jika Anda tidak mendefinisikan kolom sebagai PERSISTED, itu akan dihitung ketika permintaan Anda dijalankan.

Silakan lihat jawaban Harun untuk penjelasan dan bukti yang bagus.

Pinal Dave juga menjelaskan ini secara rinci dan menunjukkan bukti penyimpanan dalam seri-nya:

SQL SERVER - Computed Column - PERSISTED and Storage


6
Bagaimana jika mereka bertahan tetapi rencana kueri menggunakan indeks yang tidak mencakup kolom itu? Saya tidak yakin apakah Anda akan mendapatkan pencarian atau apakah hanya menghitungnya dengan cepat dan saat ini tidak dapat mengujinya.
Martin Smith

1
@ Martin Anda benar, dalam pengujian saya SQL Server memilih re-komputasi daripada pencarian.
Aaron Bertrand

34

Ini sangat mudah untuk dibuktikan sendiri. Kita bisa membuat tabel dengan kolom yang dikomputasi yang menggunakan fungsi skalar yang ditentukan pengguna, dan kemudian memeriksa rencana dan statistik fungsi sebelum dan sesudah pembaruan dan pemilihan, dan melihat kapan eksekusi direkam.

Katakanlah kita memiliki fungsi ini:

CREATE FUNCTION dbo.mask(@x varchar(32))
RETURNS varchar(32) WITH SCHEMABINDING
AS
BEGIN
  RETURN (SELECT 'XX' + SUBSTRING(@x, 3, LEN(@x)-4) + 'XXXX');
END
GO

Dan tabel ini:

CREATE TABLE dbo.Floobs
(
  FloobID int IDENTITY(1,1),
  Name varchar(32),
  MaskedName AS CONVERT(varchar(32), dbo.mask(Name)),
  CONSTRAINT pk_Floobs PRIMARY KEY(FloobID),
  CONSTRAINT ck_Name CHECK (LEN(Name)>=8)
);
GO

Mari kita periksa sys.dm_exec_function_stats(baru dalam SQL Server 2016 dan Azure SQL Database) sebelum dan setelah menyisipkan, dan kemudian setelah pilih:

SELECT o.name, s.execution_count
FROM sys.dm_exec_function_stats AS s
INNER JOIN sys.objects AS o
ON o.[object_id] = s.[object_id]
WHERE s.database_id = DB_ID();

INSERT dbo.Floobs(Name) VALUES('FrankieC');

SELECT o.name, s.execution_count
FROM sys.dm_exec_function_stats AS s
INNER JOIN sys.objects AS o
ON o.[object_id] = s.[object_id]
WHERE s.database_id = DB_ID();

SELECT * FROM dbo.Floobs;

SELECT o.name, s.execution_count
FROM sys.dm_exec_function_stats AS s
INNER JOIN sys.objects AS o
ON o.[object_id] = s.[object_id]
WHERE s.database_id = DB_ID();

Saya melihat tidak ada panggilan fungsi pada sisipan, hanya pada pilih.

Sekarang, letakkan tabel dan lakukan lagi, kali ini ubah kolom menjadi PERSISTED:

DROP TABLE dbo.Floobs;
GO
DROP FUNCTION dbo.mask;
GO

...
  MaskedName AS CONVERT(varchar(32), dbo.mask(Name)) PERSISTED,
...

Dan saya melihat yang sebaliknya terjadi: Saya mendapatkan eksekusi yang dicatat pada sisipan, tetapi tidak pada pilih.

Tidak memiliki versi SQL Server yang cukup modern untuk digunakan sys.dm_exec_function_stats? Jangan khawatir, ini ditangkap dalam rencana eksekusi juga .

Untuk versi yang tidak bertahan, kita dapat melihat fungsi yang direferensikan hanya di pilih:

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Sementara versi yang tetap hanya menunjukkan perhitungan yang terjadi pada sisipan:

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Sekarang, Martin menyampaikan poin yang bagus dalam komentar : ini tidak selalu benar. Mari kita membuat indeks yang tidak mencakup kolom yang dihitung terus-menerus, dan menjalankan kueri yang menggunakan indeks itu, dan melihat apakah pencarian mendapatkan data dari data yang ada yang ada, atau menghitung data saat runtime (drop dan buat kembali fungsi dan tabel di sini):

CREATE INDEX x ON dbo.Floobs(Name);
GO

INSERT dbo.Floobs(name) 
  SELECT LEFT(name, 32) 
  FROM sys.all_columns 
  WHERE LEN(name) >= 8;

Sekarang, kami akan menjalankan kueri yang menggunakan indeks (sebenarnya ia menggunakan indeks secara default dalam kasus khusus ini, bahkan tanpa klausa where):

SELECT * FROM dbo.Floobs WITH (INDEX(x))
  WHERE Name LIKE 'S%';

Saya melihat eksekusi tambahan dalam statistik fungsi, dan rencana itu tidak berbohong:

masukkan deskripsi gambar di sini

Jadi, jawabannya adalah ITU TERGANTUNG . Dalam hal ini, SQL Server berpikir akan lebih murah untuk menghitung kembali nilai daripada melakukan pencarian. Ini bisa berubah karena berbagai faktor, jadi jangan bergantung padanya. Dan ini dapat terjadi di kedua arah apakah fungsi yang ditentukan pengguna digunakan atau tidak; Saya hanya menggunakannya di sini karena membuatnya lebih mudah untuk diilustrasikan.


Sangat dihargai, saya tidak pernah mempertanyakan perilaku mesin dalam hasil komputasi.
Arthur D

8
@ArthurD Ini adalah keputusan pengoptimal berdasarkan (kebanyakan) pada perkiraan biaya masing-masing alternatif, lihat jawaban saya untuk pertanyaan lain di sini.
Paul White mengatakan GoFundMonica

-1

Jawaban untuk pertanyaan ini benar-benar adalah "itu tergantung." Saya baru saja menjalankan contoh di mana SQL Server menggunakan indeks pada kolom yang dihitung tetap tetapi masih menjalankan fungsi, seolah-olah nilai-nilai tidak pernah bertahan untuk memulai. Mungkin ada hubungannya dengan tipe data kolom ( nvarchar(37)) atau mungkin ukuran tabel (sekitar 7 juta baris), tetapi SQL Server memutuskan untuk mengabaikan persistedkata kunci, tampaknya, dalam contoh khusus ini.

Dalam hal ini, kunci utama pada tabel adalah TransactionID yang juga merupakan kolom yang dihitung dan tetap. Paket eksekusi menghasilkan pemindaian indeks dan dalam sebuah tabel dengan hanya 7 juta baris permintaan sederhana ini memakan waktu 2-3 menit untuk menjalankan karena fungsi dijalankan lagi di setiap baris dan nilai-nilai tampaknya tidak bertahan dalam Indeks.

membuat tabel dengan kolom yang ada rencana pelaksanaan menunjukkan fungsi sedang dilakukan

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.