Страницы

Поиск по вопросам

вторник, 5 марта 2019 г.

Первичный ключ в таблице

Повышает ли наличие первичного ключа производительность работы с таблицей, если он не участвует в условии отбора?
Если не ошибаюсь, то наличие первичного ключа задает способ хранения строк.
Если первичного ключа нет, то все хранится в куче. Так же есть некоторые особенности в использовании некластиризированных индексов на куче и без кучи.


Ответ

Сам по себе первичный ключ служит для однозначной идентификации строк в таблице, а также для ссылок на строки данной таблицы из других таблиц.
Другое дело, что в SqlServer первичный ключ по умолчанию является кластерным индексом. То есть в определении таблицы
create table TableName (ID int primary key, ... );
primary key эквивалентно primary key clustered. При желании, однако, вполне можно сделать первичный ключ и некластерным индексом, указав primary key nonclustered
Если не ошибаюсь, то наличие первичного ключа задает способ хранения строк. Если первичного ключа нет, то все хранится в куче.
Правильнее будет сформулировать - наличие или отсутствие кластерного индекса (не обязательно, чтобы он был первичным ключом).
Если на таблице нет кластерного индекса, то данные таблицы хранятся в куче без какого либо определённого порядка.
Если же на таблице есть кластерный индекс, то в нём и содержатся данные таблицы. Кластерный индекс представляет собой "B+"-дерево, в страницах которого лежат ключи в логически упорядоченном виде. В дополнение к ключам в страницах верхних уровней лежат ссылки на страницы следующего уровня, а в страницах нижнего уровня ("листьях") - данные неключевых столбцов.

Повышает ли наличие первичного ключа производительность работы с таблицей, если он не участвует в условии отбора?
Может повышать.
Сканирование кластерного первичного ключа (вообще кластерного индекса) может быть более производительным по сравнению со сканированием кучи, т.к. в кластерном индексе, в отличие от кучи, при изменении данных не создаются переадресованные записи (т.н. forwarded records).
Переадресованная запись - это такая запись, которая, увеличившись в размере при обновлении, уже не может помещаться на той странице данных, где она располагалась. В этом случае запись перемещается на другую страницу данных, а на её месте остаётся указатель. Из-за таких указателей увеличивается число чтений, при доступе к данным.
Пример.
Создадим две почти одинаковых таблицы. Одну с некластерным первичным ключом:
create table Heap ( ID int, Name varchar(50), Comments varchar(1000), constraint PK_Heap primary key nonclustered (ID) );
Другую, с такими же столбцами, но с кластерным первичным ключом:
create table Cluster ( ID int, Name varchar(50), Comments varchar(1000), constraint PK_Cluster primary key clustered (ID) );
Добавим в таблицы данных:
;with nums as ( select N = row_number() over (order by @@spid) from sys.all_columns a cross join sys.all_columns b ) insert into Cluster (ID, Name, Comments) select top (10000) N, 'Name ' + cast((N - 1) % 1000 + 1 as varchar(10)), 'Comments' from nums order by N;
insert into Heap (ID, Name, Comments) select ID, Name, Comments from Cluster;
Включив statistics io, выполним одинаковый запрос к каждой из таблиц - такой, чтобы он вызывал их полное сканирование:
set statistics io on; declare @cnt int; select @cnt = count(1) from Heap where Name = 'Name 15'; select @cnt = count(1) from Cluster where Name = 'Name 15'; set statistics io off;
Статистика показывает, что число чтений пока примерно одинаково:
Table 'Heap'. Scan count 1, logical reads 61 ... Table 'Cluster'. Scan count 1, logical reads 63 ...
Изменим данные в таблицах (что вызовет появление переадресованных записей в таблице-куче):
update Heap set Comments = replicate('More ', 50) + Comments update Cluster set Comments = replicate('More ', 50) + Comments
И повторим запросы с count и статистикой к таблицам:
Table 'Heap'. Scan count 1, logical reads 9139 ... Table 'Cluster'. Scan count 1, logical reads 680 ...
Как видим, хоть данные одни и те же, и изменялись одинаково, и запросы - одинаковые, однако, число чтений, необходимое для сканировании кучи из-за появившихся переадресованных записей выросло на порядок по сравнению с числом чтений, необходимым для сканирования кластерного индекса.

Комментариев нет:

Отправить комментарий