Когда вычисляются столбцы?

29

Когда определяются значения для вычисляемых столбцов?

  • Когда значение получено?
  • Когда значение меняется?
  • Как-нибудь в другой раз?

Я предполагаю, что это вопрос новичка, так как я ничего не нахожу в своих поисках.

Shelby115
источник

Ответы:

19

Это зависит от того, как вы определяете вычисляемый столбец. PERSISTEDВычисляемый столбец будет рассчитываться , а затем сохраняются в виде данных внутри таблицы. Если вы не определите столбец как PERSISTED, он будет рассчитан при выполнении запроса.

Пожалуйста, смотрите ответ Аарона для отличного объяснения и доказательства.

Пинал Дейв также подробно описывает это и показывает доказательство хранения в своей серии:

SQL SERVER - вычисляемый столбец - PERSISTED и хранилище

Артур Д
источник
6
А что если они сохраняются, но план запроса использует индекс, который не охватывает этот столбец? Я не уверен, что вы получите поиск или он просто вычислит его на лету и не сможет проверить его.
Мартин Смит,
1
@ Мартин, вы правы, в моем тесте SQL Server выбрал повторное вычисление вместо поиска.
Аарон Бертран
34

Это очень легко доказать самостоятельно. Мы можем создать таблицу с вычисляемым столбцом, который использует скалярную пользовательскую функцию, а затем проверить планы и статистику функций до и после обновления и выбора, и посмотреть, когда будет записано выполнение.

Допустим, у нас есть эта функция:

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 подумал, что будет дешевле пересчитать значения, чем выполнять поиск. Это может измениться из-за множества факторов, поэтому не полагайтесь на это. И это может происходить в любом направлении независимо от того, используется ли определенная пользователем функция; Я использовал это только здесь, потому что это значительно облегчило иллюстрацию.

Аарон Бертран
источник
Очень важно, что я никогда не сомневался в поведении движка при вычислении результатов.
Артур Д
8
@ArthurD Это решение оптимизатора, основанное (в основном) на предполагаемой стоимости каждой альтернативы, см. Мой ответ на другой вопрос здесь.
Пол Уайт говорит GoFundMonica
-1

Ответ на этот вопрос действительно «зависит». Я только что наткнулся на пример, где SQL Server использует индекс для сохраняемого вычисляемого столбца, но все еще выполняет функцию, как если бы значения никогда не сохранялись с самого начала. Возможно, это связано с типом данных column ( nvarchar(37)) или размером таблицы (около 7 миллионов строк), но SQL Server решил проигнорировать persistedключевое слово, как представляется, в данном конкретном случае.

В этом случае первичным ключом таблицы является TransactionID, который также является вычисляемым и постоянным столбцом. План выполнения генерирует сканирование индекса, и в таблице, содержащей только 7 миллионов строк, этот простой запрос занимает 2-3 минуты, потому что функция снова запускается для каждой строки, а значения не сохраняются в индекс.

создание таблицы с сохраненным столбцом функция выполнения плана показа выполняется

Тучная Броня
источник