Здається, це область з досить багато міфів та суперечливих поглядів.
Тож у чому різниця між змінною таблиці та локальною тимчасовою таблицею в SQL Server?
Здається, це область з досить багато міфів та суперечливих поглядів.
Тож у чому різниця між змінною таблиці та локальною тимчасовою таблицею в SQL Server?
Відповіді:
Зміст
Caveat
У цій відповіді обговорюються "класичні" змінні таблиці, введені в SQL Server 2000. SQL Server 2014 в пам'яті OLTP вводить типи таблиць, оптимізованих за пам'яттю. Вимірники змінних таблиць багато в чому відрізняються від наведених нижче! ( детальніше ).
Місце зберігання
Без різниці. Обидва зберігаються в tempdb
.
Я бачив, як було запропоновано, що для змінних таблиць це не завжди так, але це можна перевірити нижче
DECLARE @T TABLE(X INT)
INSERT INTO @T VALUES(1),(2)
SELECT sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM @T
Приклад результатів (відображення розташування в tempdb
двох рядках зберігається)
File:Page:Slot
----------------
(1:148:0)
(1:148:1)
Логічне розташування
@table_variables
поводиться більше так, ніби вони були частиною поточної бази даних, ніж #temp
це роблять таблиці. Для змінних таблиць (з 2005 року) зіставлення стовпців, якщо явно не вказано, буде поточною базою даних, тоді як для #temp
таблиць буде використовуватися порівняння за замовчуванням tempdb
( Докладніше ). Крім того, визначені користувачем типи даних та колекції XML повинні бути в tempdb для використання у #temp
таблицях, але змінні таблиці можуть використовувати їх з поточної бази даних ( Джерело ).
SQL Server 2012 представляє вміщені бази даних. поведінка тимчасових таблиць у цих відрізняється (г / т Аарон)
У вміщеній базі даних дані тимчасової таблиці порівнюються при зіставленні вміщеної бази даних.
- Усі метадані, пов'язані з тимчасовими таблицями (наприклад, назви таблиць і стовпців, індекси тощо), будуть у зіставленні каталогу.
- Названі обмеження можуть не використовуватися у тимчасових таблицях.
- Тимчасові таблиці можуть не посилатися на визначені користувачем типи, колекції XML-схем або визначені користувачем функції.
Видимість різних областей
@table_variables
можна отримати доступ лише в тій партії та обсязі, в якому вони оголошені. #temp_tables
доступні в дочірніх партіях (вкладені тригери, процедура, exec
дзвінки). #temp_tables
створений у зовнішній області ( @@NESTLEVEL=0
), може також охоплювати партії, оскільки вони зберігаються до завершення сеансу. Жоден тип об'єкта не може бути створений у дочірній партії та доступ до нього в області виклику, проте, як обговорюється далі (глобальні ##temp
таблиці можуть бути хоч).
Час життя
@table_variables
створюються неявно, коли DECLARE @.. TABLE
виконується пакет, що містить оператор (перед тим, як виконуватиметься будь-який код користувача в цій пакеті) і неявно скидається в кінці.
Хоча аналізатор не дозволить вам спробувати використати змінну таблиці перед DECLARE
твердженням, неявне створення можна побачити нижче.
IF (1 = 0)
BEGIN
DECLARE @T TABLE(X INT)
END
--Works fine
SELECT *
FROM @T
#temp_tables
створюються явно, коли трапляється CREATE TABLE
оператор TSQL , і його можна явно скинути з DROP TABLE
або буде відмінено неявно, коли пакет закінчується (якщо створено в дочірній пакеті з @@NESTLEVEL > 0
) або коли сеанс закінчується інакше.
Примітка. У межах збережених процедур обидва типи об'єктів можуть кешуватися, а не повторно створювати та випускати нові таблиці. Існують обмеження щодо того, коли це кешування може відбутися, однак їх можна порушити, #temp_tables
але які обмеження щодо @table_variables
запобігання в будь-якому разі не можна. Витрати на обслуговування кешованих #temp
таблиць трохи більше, ніж для змінних таблиць, як показано тут .
Метадані об’єкта
Це по суті однаково для обох типів об'єкта. Він зберігається в базових таблицях системи в tempdb
. #temp
Таблицю простіше бачити, проте, як OBJECT_ID('tempdb..#T')
це можна використовувати для введення в системні таблиці, і внутрішньо створене ім'я більш тісно співвідноситься з іменем, визначеним у CREATE TABLE
виписці. Для змінних таблиці object_id
функція не працює, і внутрішнє ім'я повністю генерується системою, не має відношення до імені змінної. Нижче показано, що метадані все ще є, проте ввівши назву стовпця (сподіваємось, унікального). Для таблиць без унікальних імен стовпців object_id можна визначити, використовуючи DBCC PAGE
, доки вони не порожні.
/*Declare a table variable with some unusual options.*/
DECLARE @T TABLE
(
[dba.se] INT IDENTITY PRIMARY KEY NONCLUSTERED,
A INT CHECK (A > 0),
B INT DEFAULT 1,
InRowFiller char(1000) DEFAULT REPLICATE('A',1000),
OffRowFiller varchar(8000) DEFAULT REPLICATE('B',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast('C' as varchar(max)),10000),
UNIQUE CLUSTERED (A,B)
WITH (FILLFACTOR = 80,
IGNORE_DUP_KEY = ON,
DATA_COMPRESSION = PAGE,
ALLOW_ROW_LOCKS=ON,
ALLOW_PAGE_LOCKS=ON)
)
INSERT INTO @T (A)
VALUES (1),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13)
SELECT t.object_id,
t.name,
p.rows,
a.type_desc,
a.total_pages,
a.used_pages,
a.data_pages,
p.data_compression_desc
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.tables AS t
ON t.object_id = p.object_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se'
Вихідні дані
Duplicate key was ignored.
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| object_id | name | rows | type_desc | total_pages | used_pages | data_pages | data_compression_desc |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | PAGE |
| 574625090 | #22401542 | 13 | LOB_DATA | 24 | 19 | 0 | PAGE |
| 574625090 | #22401542 | 13 | ROW_OVERFLOW_DATA | 16 | 14 | 0 | PAGE |
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | NONE |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
Операції
Операції над @table_variables
здійснюються як системні транзакції, незалежні від будь-яких транзакцій із зовнішнім користувачем, тоді як еквівалентні #temp
операції з таблицею здійснюватимуться як частина самої транзакції користувача. З цієї причини ROLLBACK
команда вплине на #temp
таблицю, але залишить @table_variable
недоторканою.
DECLARE @T TABLE(X INT)
CREATE TABLE #T(X INT)
BEGIN TRAN
INSERT #T
OUTPUT INSERTED.X INTO @T
VALUES(1),(2),(3)
/*Both have 3 rows*/
SELECT * FROM #T
SELECT * FROM @T
ROLLBACK
/*Only table variable now has rows*/
SELECT * FROM #T
SELECT * FROM @T
DROP TABLE #T
Ведення журналів
Обидва генерують записи журналу до tempdb
журналу транзакцій. Поширене помилкове уявлення про те, що це не стосується змінних таблиць, тому сценарій, що демонструє це нижче, він оголошує змінну таблиці, додає пару рядків, потім оновлює їх і видаляє.
Оскільки таблична змінна створюється та випадає неявно на початку та в кінці партії, необхідно використовувати кілька партій, щоб побачити повний журнал.
USE tempdb;
/*
Don't run this on a busy server.
Ideally should be no concurrent activity at all
*/
CHECKPOINT;
GO
/*
The 2nd column is binary to allow easier correlation with log output shown later*/
DECLARE @T TABLE ([C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3] INT, B BINARY(10))
INSERT INTO @T
VALUES (1, 0x41414141414141414141),
(2, 0x41414141414141414141)
UPDATE @T
SET B = 0x42424242424242424242
DELETE FROM @T
/*Put allocation_unit_id into CONTEXT_INFO to access in next batch*/
DECLARE @allocId BIGINT, @Context_Info VARBINARY(128)
SELECT @Context_Info = allocation_unit_id,
@allocId = a.allocation_unit_id
FROM sys.system_internals_allocation_units a
INNER JOIN sys.partitions p
ON p.hobt_id = a.container_id
INNER JOIN sys.columns c
ON c.object_id = p.object_id
WHERE ( c.name = 'C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3' )
SET CONTEXT_INFO @Context_Info
/*Check log for records related to modifications of table variable itself*/
SELECT Operation,
Context,
AllocUnitName,
[RowLog Contents 0],
[Log Record Length]
FROM fn_dblog(NULL, NULL)
WHERE AllocUnitId = @allocId
GO
/*Check total log usage including updates against system tables*/
DECLARE @allocId BIGINT = CAST(CONTEXT_INFO() AS BINARY(8));
WITH T
AS (SELECT Operation,
Context,
CASE
WHEN AllocUnitId = @allocId THEN 'Table Variable'
WHEN AllocUnitName LIKE 'sys.%' THEN 'System Base Table'
ELSE AllocUnitName
END AS AllocUnitName,
[Log Record Length]
FROM fn_dblog(NULL, NULL) AS D)
SELECT Operation = CASE
WHEN GROUPING(Operation) = 1 THEN 'Total'
ELSE Operation
END,
Context,
AllocUnitName,
[Size in Bytes] = COALESCE(SUM([Log Record Length]), 0),
Cnt = COUNT(*)
FROM T
GROUP BY GROUPING SETS( ( Operation, Context, AllocUnitName ), ( ) )
ORDER BY GROUPING(Operation),
AllocUnitName
Повертається
Наскільки мені вдалося помітити операції на обох, генерують приблизно однакові кількості журналів.
Хоча кількість журналів дуже схожа, одна важлива відмінність полягає в тому, що записи журналів, пов'язані з #temp
таблицями, не можна очистити до тих пір, поки будь-яка містить транзакцію користувача не завершиться, настільки тривала операція, що в якийсь момент записується в #temp
таблиці, не запобіжить усіченню журналу, tempdb
тоді як автономні транзакції породили для змінних таблиці не.
Змінні таблиці не підтримують, TRUNCATE
тому вони можуть бути в невигідному режимі ведення журналу, коли вимога полягає у видаленні всіх рядків із таблиці (хоча для дуже маленьких таблиць DELETE
все одно може бути краще )
Кардинальність
У багатьох планах виконання, що включають змінні таблиці, буде показано один рядок, оцінений як результат з них. Перевірка змінних властивостей таблиці показує , що SQL Server вважає , що таблиця змінні мають нульові рядки (чому він оцінює 1 рядок буде викидатися з таблиці нульовий рядки пояснюється @Paul White тут ).
Однак результати, показані в попередньому розділі, показують точний rows
підрахунок sys.partitions
. Проблема полягає в тому, що в більшості випадків заяви, що посилаються на змінні таблиці, складаються, коли таблиця порожня. Якщо оператор (повторно) компілюється після того, @table_variable
як заповнюється, то це буде використовуватись замість кардинальності таблиці (Це може статися через явний recompile
або, можливо, тому, що в операторі також посилається на інший об'єкт, який викликає відкладену компіляцію або перекомпіляцію.)
DECLARE @T TABLE(I INT);
INSERT INTO @T VALUES(1),(2),(3),(4),(5)
CREATE TABLE #T(I INT)
/*Reference to #T means this statement is subject to deferred compile*/
SELECT * FROM @T WHERE NOT EXISTS(SELECT * FROM #T)
DROP TABLE #T
План показує точну оціночну кількість рядків після відкладеної компіляції.
У SQL Server 2012 SP2 введено прапор сліду 2453. Більш детальну інформацію ви можете знайти у розділі "Реляційний двигун" тут .
Якщо цей прапор слідів увімкнено, він може спричинити автоматичні перекомпіляції, щоб врахувати змінену кардинальність, про що йшлося далі дуже скоро.
Примітка: На Azure на рівні сумісності 150 компіляція оператора відкладена до першого виконання . Це означає, що вона більше не буде піддаватися проблемі оцінки нульового ряду.
Немає статистики стовпців
Наявність більш точної кардинальності таблиці не означає, що оцінене число рядків буде більш точним, однак (якщо не робити операцію з усіма рядками таблиці). SQL Server взагалі не підтримує статистику стовпців для змінних таблиць, тому повернеться до здогадок на основі предиката порівняння (наприклад, 10% таблиці буде повернуто =
проти не унікального стовпця або 30% для >
порівняння). На відміну від статистики стовпців будуть підтримуватися для #temp
таблиць.
SQL Server підтримує кількість змін, внесених до кожного стовпця. Якщо кількість змін з моменту складання плану перевищує поріг рекомпіляції (RT), план буде перекомпільовано та статистика оновлена. RT залежить від типу та розміру таблиці.
Від кешування плану в SQL Server 2008
RT розраховується наступним чином. (n посилається на кардинальність таблиці, коли складається план запитів.)
Постійна таблиця
- Якщо n <= 500, RT = 500.
- Якщо n> 500, RT = 500 + 0,20 * n.Тимчасова таблиця
- Якщо n <6, RT = 6.
- Якщо 6 <= n <= 500, RT = 500.
- Якщо n> 500, RT = 500 + 0,20 * n.
Таблична змінна
- RT не існує. Тому рекомпіляції не відбуваються через зміни кардинальності табличних змінних. (Але див. Примітку про TF 2453 нижче)
KEEP PLAN
натяк може бути використаний для установки RT для #temp
таблиць таких же , як для постійних таблиць.
Чистий ефект всього цього полягає в тому, що часто плани виконання, створені для #temp
таблиць, на порядок краще, ніж для @table_variables
тих випадків, коли задіяно багато рядків, оскільки SQL Server має кращу інформацію для роботи.
NB1: Змінні таблиці не мають статистичних даних, але все ще можуть мати місце події перекомпіляції "Статистика змінена" під прапором сліду 2453 (не застосовується для "тривіальних" планів). додатковий, що якщо N=0 -> RT = 1
. тобто всі заяви, складені, коли змінна таблиця порожня, в кінцевому підсумку отримують рекомпіляцію та виправляють TableCardinality
перший раз, коли вони виконуються, коли не порожні. Кардинальність таблиці складання зберігається в плані, і якщо оператор знову виконується з тією ж кардинальністю (або через потоки операторів керування, або повторного використання кешованого плану), повторна компіляція не відбувається.
NB2: Для кешованих тимчасових таблиць у збережених процедурах історія перекомпіляції набагато складніше, ніж описано вище. Дивіться тимчасові таблиці в Збережених процедурах для всіх деталей горіхів.
Переклади
Як і перекомпіляції на основі модифікацій, описані вище, #temp
таблиці також можуть бути пов’язані з додатковими компіляціями просто тому, що вони дозволяють виконувати операції, заборонені для змінних таблиць, які викликають компіляцію (наприклад, зміни DDL CREATE INDEX
, ALTER TABLE
)
Блокування
Було заявлено, що змінні таблиці не беруть участі в блокуванні. Це не так. Запустивши нижче виходи на вкладку Повідомлення SSMS, дані про блокування, зняті та випущені для оператора вставлення.
DECLARE @tv_target TABLE (c11 int, c22 char(100))
DBCC TRACEON(1200,-1,3604)
INSERT INTO @tv_target (c11, c22)
VALUES (1, REPLICATE('A',100)), (2, REPLICATE('A',100))
DBCC TRACEOFF(1200,-1,3604)
Для запитів SELECT
із табличних змінних Пол Уайт в коментарях зазначає, що вони автоматично надходять із неявним NOLOCK
підказкою. Це показано нижче
DECLARE @T TABLE(X INT);
SELECT X
FROM @T
OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8607)
*** Output Tree: (trivial plan) ***
PhyOp_TableScan TBL: @T Bmk ( Bmk1000) IsRow: COL: IsBaseRow1002 Hints( NOLOCK )
Однак вплив цього на фіксацію може бути незначним.
SET NOCOUNT ON;
CREATE TABLE #T( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @T TABLE ( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @I INT = 0
WHILE (@I < 10000)
BEGIN
INSERT INTO #T DEFAULT VALUES
INSERT INTO @T DEFAULT VALUES
SET @I += 1
END
/*Run once so compilation output doesn't appear in lock output*/
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEON(1200,3604,-1)
SELECT *, sys.fn_PhysLocFormatter(%%physloc%%)
FROM @T
PRINT '--*--'
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEOFF(1200,3604,-1)
DROP TABLE #T
Жоден із цих повернень не призводить до порядку індексного ключа, що вказує на те, що SQL Server використовував сканований розподілом сканування для обох.
Я запускав вищезазначений сценарій двічі, а результати другого запуску нижче
Process 58 acquiring Sch-S lock on OBJECT: 2:-1325894110:0 (class bit0 ref1) result: OK
--*--
Process 58 acquiring IS lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 acquiring S lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 releasing lock on OBJECT: 2:-1293893996:0
Вихід блокування для змінної таблиці дійсно надзвичайно мінімальний, оскільки SQL Server просто отримує блокування стабільності схеми на об'єкті. Але для #temp
столу він майже такий же легкий, що він виймає S
блокування рівня об'єкта . Звичайно, NOLOCK
підказка або READ UNCOMMITTED
рівень ізоляції можна чітко вказати і під час роботи з #temp
таблицями.
Аналогічно проблемі з реєстрацією транзакції оточуючого користувача може означати, що блоки зберігаються довше для #temp
таблиць. Зі сценарієм нижче
--BEGIN TRAN;
CREATE TABLE #T (X INT,Y CHAR(4000) NULL);
INSERT INTO #T (X) VALUES(1)
SELECT CASE resource_type
WHEN 'OBJECT' THEN OBJECT_NAME(resource_associated_entity_id, 2)
WHEN 'ALLOCATION_UNIT' THEN (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.allocation_units a
JOIN tempdb.sys.partitions p ON a.container_id = p.hobt_id
WHERE a.allocation_unit_id = resource_associated_entity_id)
WHEN 'DATABASE' THEN DB_NAME(resource_database_id)
ELSE (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.partitions
WHERE partition_id = resource_associated_entity_id)
END AS object_name,
*
FROM sys.dm_tran_locks
WHERE request_session_id = @@SPID
DROP TABLE #T
-- ROLLBACK
при запуску поза явною транзакцією користувача для обох випадків єдиним блокуванням, що повертається при перевірці, sys.dm_tran_locks
є спільний замок на DATABASE
.
Після коментарів BEGIN TRAN ... ROLLBACK
26 рядків повертаються, показуючи, що блокування проводяться як на самому об'єкті, так і на рядках системної таблиці, щоб дозволити відкат і запобігти іншим транзакціям зчитування недозволених даних. Еквівалентна операція змінної таблиці не підлягає відскоку з трансакцією користувача та не має потреби утримувати ці блокування для нас, щоб перевірити наступне висловлення, але відстеження замків, придбаних та випущених у Profiler, або використання прапора трассингу 1200, показує, що ще багато подій блокування роблять досі трапляються.
Покажчики
Для версій до SQL Server 2014 індекси можна створювати лише неявно на змінних таблиці як побічний ефект додавання унікального обмеження або первинного ключа. Це, звичайно, означає, що підтримуються лише унікальні індекси. Не унікальний некластеризований індекс на таблиці з унікальним кластерним індексом можна моделювати, однак просто оголосивши його UNIQUE NONCLUSTERED
та додавши ключ CI до кінця потрібного ключа NCI (SQL Server все одно зробив би це за кадром, навіть якщо не унікальний NCI можна вказати)
Як було показано раніше, різні index_option
s можуть бути вказані в декларації обмежень, включаючи DATA_COMPRESSION
, IGNORE_DUP_KEY
і FILLFACTOR
(хоча немає сенсу встановлювати це, оскільки це може змінити лише відновлення індексу, і ви не можете перебудовувати індекси на змінних таблиці!)
Додатково змінні таблиці не підтримують INCLUDE
d стовпців, відфільтровані індекси (до 2016 року) або розділення, як #temp
це робиться в таблицях (схема розділу повинна бути створена в tempdb
).
Індекси в SQL Server 2014
Не унікальні індекси можуть бути оголошені вбудованими у визначенні змінної таблиці в SQL Server 2014. Приклад синтаксису для цього наведено нижче.
DECLARE @T TABLE (
C1 INT INDEX IX1 CLUSTERED, /*Single column indexes can be declared next to the column*/
C2 INT INDEX IX2 NONCLUSTERED,
INDEX IX3 NONCLUSTERED(C1,C2) /*Example composite index*/
);
Індекси в SQL Server 2016
З CTP 3.1 тепер можна оголосити відфільтровані індекси для змінних таблиці. За допомогою RTM можливо, що включені стовпці також дозволені, хоча вони , ймовірно, не перетворять його в SQL16 через обмеження ресурсів.
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)
Паралелізм
Запити, які вставляються у (або іншим чином змінюються), @table_variables
не можуть мати паралельний план, #temp_tables
не обмежуються таким чином.
У цьому переписуванні є очевидне вирішення, як це дає змогу SELECT
деталі проходити паралельно, але це закінчується використанням прихованої тимчасової таблиці (за лаштунками)
INSERT INTO @DATA ( ... )
EXEC('SELECT .. FROM ...')
Не існує такого обмеження в запитах, які вибирають із змінних таблиць, як показано в моїй відповіді тут
Інші функціональні відмінності
#temp_tables
не можна використовувати всередині функції. @table_variables
може використовуватися всередині скалярних UDF скалярних або багатосказальних таблиць.@table_variables
не міг назвати обмеження.@table_variables
не може бути SELECT
-ed INTO
, ALTER
-ed, TRUNCATE
d або бути ціллю DBCC
команд, таких як DBCC CHECKIDENT
або, SET IDENTITY INSERT
і не підтримувати підказки таблиці, наприкладWITH (FORCESCAN)
CHECK
обмеження щодо змінних таблиць оптимізатором не враховуються для спрощення, маються на увазі предикати або виявлення суперечностей.PAGELATCH_EX
очікування. ( Приклад )Пам'ять лише?
Як зазначено на початку, обидва зберігаються на сторінках у tempdb
. Однак я не звертався до того, чи була якась різниця в поведінці, коли справа стосується написання цих сторінок на диск.
Я зробив невелику кількість тестувань на цьому зараз, і поки що не бачив такої різниці. У конкретному тесті, який я робив на моєму екземплярі SQL Server, 250 сторінок здається точкою відсічення до того, як файл даних буде записаний.
Примітка: Поведінка нижче, більше не зустрічається в SQL Server 2014 або SQL Server 2012 SP1 / CU10 або SP2 / CU1, прагне письменника вже не так хочеться писати сторінки на диск. Детальніше про цю зміну на SQL Server 2014: tempdb Hemden Performance Gem .
Запуск сценарію нижче
CREATE TABLE #T(X INT, Filler char(8000) NULL)
INSERT INTO #T(X)
SELECT TOP 250 ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values
DROP TABLE #T
І моніторинг записує у tempdb
файл даних за допомогою Монітора процесів, я не бачив жодного (за винятком випадків, коли вони завантажуються на сторінку завантаження бази даних зі зміщенням 73 728). Після переходу 250
на 251
почав бачити, як пише нижче.
На знімку екрана показано запис 5 * 32 сторінок і один запис на одній сторінці, який вказує на те, що 161 сторінок було записано на диск. Я отримав однакову точку відрізу в 250 сторінок під час тестування із змінними таблиці. Сценарій нижче показує це по-іншому, дивлячисьsys.dm_os_buffer_descriptors
DECLARE @T TABLE (
X INT,
[dba.se] CHAR(8000) NULL)
INSERT INTO @T
(X)
SELECT TOP 251 Row_number() OVER (ORDER BY (SELECT 0))
FROM master..spt_values
SELECT is_modified,
Count(*) AS page_count
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = (SELECT a.allocation_unit_id
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se')
GROUP BY is_modified
is_modified page_count
----------- -----------
0 192
1 61
Показано, що на диску було написано 192 сторінки, а брудний прапор очищено. Це також показує, що написання на диск не означає, що сторінки будуть вилучені з буферного пулу негайно. Запити щодо цієї змінної таблиці все ще можна задовольнити повністю з пам'яті.
На холостому сервері з max server memory
налаштованими 2000 MB
та DBCC MEMORYSTATUS
звітнішими сторінками буферного пулу, розміщеними приблизно 1843 000 Кб (приблизно 23 000 сторінок), я вставив до таблиць вище в партіях по 1000 рядків / сторінок і за кожну записану ітерацію.
SELECT Count(*)
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = @allocId
AND page_type = 'DATA_PAGE'
І змінна таблиця, і #temp
таблиця давали майже однакові графіки і встигли значно збільшити максимум буферного пулу, перш ніж дійти до того, що вони не були повністю збережені в пам’яті, тому, схоже, немає особливого обмеження в кількості пам'яті або можна споживати.
Я хотів би зазначити кілька речей, що базуються більше на конкретному досвіді, а не на навчанні. Як DBA, я дуже новачок, тому, будь ласка, виправте мене там, де потрібно.