Коли обчислюються колонки?


29

Коли визначаються значення для обчислених стовпців?

  • Коли значення буде отримано?
  • Коли значення змінюється?
  • Іншим разом?

Я здогадуюсь, що це питання новачків, оскільки я нічого не знаходжу в своїх пошуках.

Відповіді:


19

Це залежить від того, як ви визначаєте обчислений стовпець. PERSISTEDОбчислюваний стовпець буде розраховуватися , а потім зберігаються в вигляді даних всередині таблиці. Якщо ви не визначите стовпець як PERSISTED, він буде обчислюватися, коли виконується ваш запит.

Будь ласка, дивіться відповідь Аарона для великого пояснення та доказу.

Піналь Дейв також детально описує це і показує докази зберігання у своїй серії:

SQL SERVER - Обчислена колонка - ПЕРСИСТИЧНА І Зберігання


6
Що робити, якщо вони зберігаються, але план запитів використовує індекс, який не охоплює цей стовпець? Я не впевнений, чи отримаєте ви пошук, чи буде він просто обчислити його на льоту і наразі не може перевірити його.
Мартін Сміт

1
@Martin ви маєте рацію, у моєму тесті 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 та базах даних Azure SQL) до та після вставки, а потім після вибору:

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();

Я не бачу жодного виклику функції на вставці, лише на select.

Тепер відкиньте таблиці і зробіть це знову, цього разу змінивши стовпчик на 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? Не хвилюйтесь, це також зафіксовано в планах розстрілу .

Для непостійної версії ми можемо бачити функцію, на яку посилається лише у вибраному:

введіть тут опис зображення

введіть тут опис зображення

Хоча збережена версія показує лише обчислення, що відбуваються при вставці:

введіть тут опис зображення

введіть тут опис зображення

Тепер Мартін наводить чудовий момент у коментарі : це не завжди буде правдою. Створимо індекс, який не охоплює збережений обчислюваний стовпець, і запустимо запит, який використовує цей індекс, і подивимось, чи шукає дані отримані дані з існуючих збережених даних, або обчислює дані під час виконання (функція "падіння та відновлення". і таблиця тут):

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

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

Тепер ми запустимо запит, який використовує індекс (фактично він використовує індекс за замовчуванням у будь-якому випадку, навіть без пункту де):

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

Я бачу додаткові виконання у статистиці функцій, і план не лежить:

введіть тут опис зображення

Отже, відповідь - це ЗАЛЕЖЕННЯ . У цьому випадку SQL Server вважав, що дешевше повторно обчислити значення, ніж виконати пошук. Це може змінитися через різноманітні фактори, тому не покладайтеся на це. І це може статися в будь-якому напрямку, незалежно від того, використовується або не визначена користувачем функція; Я використовував його лише тому, що це значно полегшило зображення.


Багато вдячний, я ніколи не сумнівався в поведінці двигуна при обчисленні результатів.
Артур Д

8
@ArthurD Це рішення оптимізатора, яке ґрунтується (в основному) на кошторисних витратах кожної альтернативи, дивіться мою відповідь на інше питання тут.
Пол Білий каже, що GoFundMonica

-1

Відповідь на це питання справді - це "залежить". Я щойно наштовхнувся на приклад, коли SQL Server використовує індекс на збереженій обчисленій колонці, але він все ще виконує функцію, як ніби значення ніколи не зберігалися. Це може стосуватися типу даних стовпця ( nvarchar(37)) або, можливо, розміру таблиці (близько 7 мільйонів рядків), але SQL Server вирішив ігнорувати persistedключове слово, виявляється, саме в цьому конкретному випадку.

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

створення таблиці зі збереженим стовпцем виконується функція відображення плану виконання

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.