Jawaban:
Itu tergantung pada bagaimana Anda mendefinisikan kolom yang dihitung. Sebuah PERSISTED
kolom 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:
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:
Sementara versi yang tetap hanya menunjukkan perhitungan yang terjadi pada sisipan:
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:
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.
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 persisted
kata 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.