Ответы:
Это зависит от того, как вы определяете вычисляемый столбец. PERSISTED
Вычисляемый столбец будет рассчитываться , а затем сохраняются в виде данных внутри таблицы. Если вы не определите столбец как PERSISTED
, он будет рассчитан при выполнении запроса.
Пожалуйста, смотрите ответ Аарона для отличного объяснения и доказательства.
Пинал Дейв также подробно описывает это и показывает доказательство хранения в своей серии:
Это очень легко доказать самостоятельно. Мы можем создать таблицу с вычисляемым столбцом, который использует скалярную пользовательскую функцию, а затем проверить планы и статистику функций до и после обновления и выбора, и посмотреть, когда будет записано выполнение.
Допустим, у нас есть эта функция:
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
И эта таблица:
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
Давайте проверим sys.dm_exec_function_stats
(новое в SQL Server 2016 и базе данных SQL Azure) до и после вставки, а затем после выбора:
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();
Я не вижу вызова функции на вставке, только на выбор.
Теперь удалите таблицы и сделайте это снова, на этот раз изменив столбец на PERSISTED
:
DROP TABLE dbo.Floobs;
GO
DROP FUNCTION dbo.mask;
GO
...
MaskedName AS CONVERT(varchar(32), dbo.mask(Name)) PERSISTED,
...
И я вижу, что происходит обратное: я записываю выполнение на вставку, но не на выборку.
Не имеете достаточно современной версии SQL Server для использования sys.dm_exec_function_stats
? Не беспокойтесь, это также отражено в планах выполнения .
Для непостоянной версии мы можем видеть функцию, на которую ссылаются только в select:
В то время как постоянная версия показывает только вычисления, происходящие при вставке:
Теперь Мартин приводит замечательный момент в комментарии : это не всегда будет правдой. Давайте создадим индекс, который не покрывает сохраняемый вычисляемый столбец, и запустим запрос, который использует этот индекс, и посмотрим, получает ли поиск данные из существующих постоянных данных, или вычисляет данные во время выполнения (функция удаления и повторного создания) и таблица здесь):
CREATE INDEX x ON dbo.Floobs(Name);
GO
INSERT dbo.Floobs(name)
SELECT LEFT(name, 32)
FROM sys.all_columns
WHERE LEN(name) >= 8;
Теперь мы запустим запрос, который использует индекс (на самом деле он использует индекс по умолчанию в этом конкретном случае, даже без предложения where):
SELECT * FROM dbo.Floobs WITH (INDEX(x))
WHERE Name LIKE 'S%';
Я вижу дополнительные исполнения в статистике функций, и план не лжет:
Итак, ответ ЭТО ЗАВИСИТ . В этом случае SQL Server подумал, что будет дешевле пересчитать значения, чем выполнять поиск. Это может измениться из-за множества факторов, поэтому не полагайтесь на это. И это может происходить в любом направлении независимо от того, используется ли определенная пользователем функция; Я использовал это только здесь, потому что это значительно облегчило иллюстрацию.
Ответ на этот вопрос действительно «зависит». Я только что наткнулся на пример, где SQL Server использует индекс для сохраняемого вычисляемого столбца, но все еще выполняет функцию, как если бы значения никогда не сохранялись с самого начала. Возможно, это связано с типом данных column ( nvarchar(37)
) или размером таблицы (около 7 миллионов строк), но SQL Server решил проигнорировать persisted
ключевое слово, как представляется, в данном конкретном случае.
В этом случае первичным ключом таблицы является TransactionID, который также является вычисляемым и постоянным столбцом. План выполнения генерирует сканирование индекса, и в таблице, содержащей только 7 миллионов строк, этот простой запрос занимает 2-3 минуты, потому что функция снова запускается для каждой строки, а значения не сохраняются в индекс.