В предыдущей лекции вы научились извлекать сводную информацию о данных, хранящихся в вашей базе данных. SQL Server может возвращать результаты, в том числе, сводную информацию, быстро и эффективно, если данные правильно хранятся в базе данных. В этой лекции объясняются различные способы хранения и извлечения данных в SQL Server, а также рассказывается о том, какие факторы следует учитывать при разработке базы данных, чтобы добиться наиболее эффективной производительности от SQL Server.
Когда сервер SQL Server выполняет запрос, сначала требуется определить наилучший способ выполнения. Для этого нужно рассчитать, как и в каком порядке обращаться к данным и соединять их, как и когда выполнять вычисления и агрегации и т. д. За это отвечает подсистема, которая называется
Сможет ли
Как видите, генерирование плана выполнения запросов - это функция, немаловажная для производительности SQL Server, поскольку эффективность плана выполнения запроса определяет, будет ли время его выполнения измеряться в миллисекундах, секундах или даже минутах. Планы выполнения запросов, которые показали низкую скорость выполнения, можно проанализировать, чтобы определить, имеется ли индекс, устарели ли данные статистики или просто SQL Server выбрал не самый эффективный план (такое случается не очень часто).
Adventure Works, выбрав ее из раскрывающегося списка Available Databases (Доступные базы данных).SELECT. Код этого примера имеется в файлах примеров под именем Viewing Query Plans .sql.SELECT SalesOrderID, OrderQTY FROM Sales.SalesOrderDetail WHERE ProductID = 712 ORDER BY OrderQTY DESC

При генерировании предполагаемого плана запроса запрос на самом деле не выполняется. Он только оптимизируется Оптимизатором запроса. Эта особенность Оптимизатора запросов является преимуществом, когда приходится иметь дело с запросами, которые имеют продолжительные
Sort (Сортировка), который сортирует данные на основе предложения ORDER BY.Мы рассмотрим самые важные операторы, которые использует SQL Server, когда будем изучать индексы и соединения. Полный список операторов можно найти в Электронной документации SQL Server 2005, тема "Пиктограммы графического представления плана выполнения".
Стоимость в процентах под пиктограммой каждого оператора показывает процент от общей стоимости запроса, представленного на графической схеме. Это число поможет вам понять, какая операция использует при выполнении больше всего ресурсов. В нашем случае самой дорогостоящей операцией является Clustered Index Scan (Просмотр
В этом окне отображается подробная информация об операции. До сих пор мы знали только то, что SQL Server извлекает данные при помощи операции сканирования. Но в этом окне видно, что он выполняет операцию Clustered Index Scan (Просмотр Sales.SalesOrderDetail, а также поиск ProductID 712. Эта информация находится в секции Predicates (Предикаты). Кроме того, показаны предполагаемая стоимость и предполагаемое количество строк, а также размер строки. В то время, как количество строк оценивается на основе статистики, которую SQL Server хранит для этой таблицы, значения стоимости вычисляются на основе статистики и значений эталонной системы. Следовательно, значения стоимости не следует использовать для того, чтобы рассчитать, сколько времени запрос будет выполняться на компьютере. Эти цифры могут использоваться только для выявления более дешевой или более дорогостоящей операции.
.sqlplan. Его можно открыть через SQL Server Management Studio. выбрав из меню File (Файл) команды Open, File (Открыть, Файл).Теперь мы рассмотрим, как работают индексы и как они повышают производительность запросов. В SQL Server 2005 можно определить два различных типа индексов: кластеризованные и некластеризованные. Чтобы понять, как индексы могут ускорить доступ к данным и какие типы индексов использовать в конкретных ситуациях, необходимо понимать, как данные и индексы хранятся в файлах данных и как SQL Server осуществляет доступ к данным в файлах данных.
Файл данных в базе данных SQL Server делится на страницы по 8 Кбайт. Каждая страница содержит данные, индексы или другие типы данных, которые нужны SQL Server, чтобы манипулировать файлами данных. Однако большая часть страниц - это страницы данных или индекса. Страницы представляют собой единицы, которые SQL Server считывает и записывает из файлов данных. Каждая страница содержит данные или информацию индекса только для одного объекта базы данных. Поэтому на каждой странице данных вы найдете данные об одном объекте, а на каждой странице индекса - только информацию индекса. В SQL Server 2000 невозможно разбить строки данных на несколько страниц; это означает, что строка данных должна уместиться на странице, что ограничивает размер строки примерно 8060 байт (за исключением больших объектов данных). В SQL Server 2005 это ограничение больше не существует для типов данных переменной длины, таких, как nvarchar, varbinary, CLR и т. п. Благодаря типам данных переменной длины строки могут занимать несколько страниц, но все строки с типом данных фиксированной длины все же должны вписываться в одну страницу.
Когда пользователь создает таблицу и вносит в нее данные, SQL Server осуществляет поиск неиспользуемых страниц, на которых можно сохранить данные. Чтобы отслеживать, какие страницы содержат данные для таблиц, SQL Server для каждой таблицы хранит еще одну или больше дополнительных страниц IAM, Index
Adventure Works.dbo.Orders и dbo.OrderDetails. Для создания таблиц и заполнения их данными ведите и выполните следующие инструкции. Код для всего примера можно найти среди файлов примеров под именем Examining Heap Structures.sql.USE AdventureWorks;
GO
CREATE TABLE dbo.Orders( SalesOrderID int NOT NULL,
OrderDate datetime NOT NULL,
ShipDate datetime NULL,
Status tinyint NOT NULL,
PurchaseOrderNumber dbo.OrderNumber NULL,
CustomerID int NOT NULL,
ContactID int NOT NULL,
SalesPersonID int NULL
);
CREATE TABLE dbo.OrderDetails( SalesOrderID int NOT NULL,
SalesOrderDetailID int NOT NULL,
CarrierTrackingNumber nvarchar(25),
OrderQty smallint NOT NULL,
ProductID int NOT NULL,
UnitPrice money NOT NULL,
UnitPriceDiscount money NOT NULL,
LineTotal AS (isnull((UnitPrice*((1.0)-UnitPriceDiscount))*OrderQty,(0.0)))
);
INSERT INTO dbo.Orders
SELECT SalesOrderID, OrderDate, ShipDate, Status, PurchaseOrderNumber,
CustomerID, ContactID, SalesPersonID
FROM Sales.SalesOrderHeader;
INSERT INTO dbo.OrderDetails(SalesOrderID, SalesOrderDetailID,
CarrierTrackingNumber, OrderQty,
ProductID, UnitPrice, UnitPriceDiscount)
SELECT SalesOrderID, SalesOrderDetailID,CarrierTrackingNumber,OrderQty,
ProductID, UnitPrice, UnitPriceDiscount
FROM Sales.SalesOrderDetail;
dbo.Orders введите инструкции, которые приводятся ниже. Включите действительный план выполнения, нажав (Ctrl+M) до начала выполнения или выбрав из меню Query (Запрос) команду Include Actual Execution Plan (Включить действительный план выполнения). Выполните запрос:SET STATISTICS IO ON; SELECT * FROM dbo.Orders SET STATISTICS IO OFF
Параметр SET STATISTICS IO включает функцию, которая вызывает отправку сообщений о выполненных операциях дискового ввода/ вывода обратно клиенту сервером SQL Server при выполнении инструкции. Это замечательная функция, которую следует использовать для определения стоимости операций ввода/вывода для запросов.

Этот вывод информирует, что SQL Server для данной операции должен просмотреть данные таблицы один раз, причем нужно выполнить 178 считываний страниц (логических чтений). Этот вывод показывает также, что физические считывания для выполнения этой операции не используются (физические, или опережающие считывания). Физических считываний не было потому, что, в данном случае, данные уже находились в буферном кэше. Если окно Messages (Сообщения) показывает, что в данном запросе выполнялись физические считывания, то выполните запрос еще раз; вы увидите, что количество физических считываний будет меньше, чем было до этого. Причина заключается в том, что SQL Server хранит страницы данных, к которым недавно были обращения, в буферном кэше для повышения производительности.

SET STATISTICS IO ON; SELECT * FROM dbo.Orders WHERE SalesOrderID =46699; SET STATISTICS IO OFF;
Чтобы повысить производительность доступа к данным, следует определить индексы в столбцах. Столбец, в котором задан индекс, называется ключевым столбцом индекса. Индекс, встроенный в столбец, похож на оглавление книги. Он включает отсортированные значения из этого столбца и ссылки на страницы, на которых можно найти действительные строки и данные.
Чтобы найти строки, в которых столбец индекса имеет определенное значение, SQL Server должен будет найти в индексе это значение, а затем перейти по указателю, чтобы прочитать строку. Эта операция намного проще и дешевле, чем просмотр всех данных, который был выполнен нами при выполнении просмотра таблицы.
Индексы в SQL Server встроены в древовидную структуру, которая называется сбалансированным деревом. Основная структура сбалансированного дерева показана ниже в примере. Как видите, нижний уровень называется уровнем листовых вершин. Уровень листовых вершин можно мыслить как оглавление книги. Он содержит по одной записи на каждую строку данных, причем записи сортируются по столбцу индекса. Чтобы ускорить поиск значений в индексе, дерево построено поверх него с использованием операций сравнения < (меньше чем) и > (больше чем). Число уровней индекса зависит от количества записей и размера ключа индекса. В реальных условиях страница индекса могла бы содержать намного больше значений, чем изображено в примере. Поскольку страница имеет размер 8 Кбайт, SQL Server может указать на странице индекса на тысячи страниц. Следовательно, индекс обычно не имеет много уровней, даже если таблица содержит миллионы строк. Этот факт способствует очень быстрому поиску определенных значений.
Надписи: Root (Level 2) - Уровень корневой вершины (2 уровень) Intermediate (Level 1) - Уровень внутренних вершин (1 уровень) Leaf Level (Level 0) - Уровень листовых вершин (0 уровень) KEY Pointer - Ключ-указатель
Как уже отмечалось ранее, в SQL Server используется два типа индексов: кластеризованный и некластеризованный. Оба типа индексов представляют собой сбалансированные деревья, но построены они по-разному. Давайте посмотрим, в чем заключается разница.
Поскольку данные включены в
CREATE [ UNIQUE ] CLUSTERED INDEX index name ON <object> ( column [ ASC | DESC ] [ ,...n ] )
Как видите, можно определить индекс как уникальный, в этом случае одинаковые значения ключа индекса в двух строках недопустимы.
CREATE или ALTER TABLE с ключевыми словами CLUSTERED или NONCLUSTERED.Adventure Works.Orders ведите и выполните следующую инструкцию. Код этого примера имеется в файлах примеров под именем Creating And Using Clustered Indexes.sql.CREATE UNIQUE CLUSTERED INDEX CLIDX_Orders_SalesOrderID
ON dbo.Orders(SalesOrderID)
SELECT, как делали это раньше, и изучите различия. Обязательно включите при выполнении запроса действительный план выполнения.SET STATISTICS IO ON; SELECT * FROM dbo.Orders; SELECT * FROM dbo.Orders WHERE SalesOrderID =46699; SET STATISTICS IO OFF;

Мы видим, что SQL Server больше не использует просмотры таблицы. Теперь он выполняет операции индексирования, потому что данные больше не хранятся в структуре кучи. План выполнения запроса показывает, что в данном случае используются две основные операции с индексами из всех возможных:
SELECT не имеет предложения WHERE, серверу SQL Server известно, что нужно возвратить все данные, которые хранятся на уровне листовых вершин индекса.Эти две операции могут также соединяться, чтобы извлечь определенный диапазон данных. В этом виде операции частичного просмотра SQL Server пытается найти начало диапазона, а затем просматривает дерево до конца этого диапазона.

Мы видим, что первая инструкция SELECT генерирует почти тот же объем считываний страниц, что и операция просмотра таблицы, если у нее структура кучи. Это не удивительно, поскольку инструкция SELECT требует все данные, и, следовательно, SQL Server должен возвратить все данные. Но второй запрос генерирует только два считывания страниц, а это значительное улучшение по сравнению с теми 178 считываниями страниц, которые были показаны раньше. SQL Server необходимо только выполнить поиск по индексу, что требует гораздо меньшего количества операций ввода/вывода, чем поиск в каждой странице данных.
SELECT, которая возвращает данные в отсортированной форме, и нажмите (Ctrl+L), чтобы получить предполагаемый план выполнения.SELECT * FROM dbo.Orders ORDER BY SalesOrderID; SELECT * FROM dbo.Orders ORDER BY OrderDate;

Из рисунка видно, что первая инструкция выполняет только просмотр SalesOrderID, потому что этот столбец является ключом
Во втором запросе данные были отсортированы после получения. Следовательно, после операции просмотра Sort (Сортировка), которая отсортировала данные по столбцу OrderDate. Поскольку сортировка является очень дорогостоящей операцией, второй запрос генерирует 93% общей стоимости обоих запросов. Таким образом, имеет смысл определять
Теперь создадим составной индекс в таблице OrderDetails. Составной индекс - это индекс, который определен более, чем для одного столбца.
OrderDetails.CREATE UNIQUE CLUSTERED INDEX CLIDX_OrderDetails ON dbo.OrderDetails(SalesOrderID,SalesOrderDetailID)
SELECT. Первая будет выполнять поиск указанного значения SalesOrderID, а вторая -указанного значения SalesOrderDetailID.
Оба столбца являются индексными столбцами нашего индекса CLIDX_OrderDetails.
Нажмите (Ctrl+L), чтобы отобразить предполагаемый план выполнения.SELECT * FROM dbo.OrderDetails WHERE SalesOrderID = 46999 SELECT * FROM dbo.OrderDetails WHERE SalesOrderDetailID = 14147
Легко заметить, что в первом запросе, который выполняет поиск значения из первого столбца составного индекса, SQL Server для поиска строки использует поиск по индексам (метод seek). Во втором запросе он использует просмотр индекса, который является более дорогостоящей операцией. Просмотр индекса используется потому, что невозможно искать значение только во втором столбце составного индекса, ведь индекс изначально сортируется по первому столбцу. Следовательно, важно продумать порядок индексных столбцов в составном индексе. Запомните, что составной индекс следует использовать только тогда, когда поиск по дополнительным столбцам выполняется исключительно в сочетании с первым столбцом или если должна быть применена уникальность.
В противоположность кластеризованным индексам
Поскольку
CREATE [ UNIQUE ] NONCLUSTERED INDEX index name
ON <object> ( column [ ASC | DESC ] [ ,...n ] )
Adventure Works.SELECT и нажмите клавиатурную комбинацию (Ctrl+L), чтобы отобразить предполагаемый план выполнения. Код этого примера имеется в файлах примеров под именем Creating And Using Nonclustered Indexes.sql.SELECT DISTINCT SalesOrderID, CarrierTrackingNumber FROM dbo.OrderDetails WHERE ProductID = 776
SQL Server выполняет операцию просмотра OrderDetails нет индекса. Чтобы ускорить выполнение этого запроса, SQL Server потребуется индекс в столбце ProductID. Поскольку OrderDetails уже определен, приходится использовать
Sort (Сортировка) применяется в этом плане выполнения запроса для получения неповторяющегося результата.Product ID таблицы OrderDetail, введите и выполните следующую инструкцию:CREATE INDEX NCLIX_OrderDetails_ProductID ON dbo.OrderDetails(ProductID)
SELECT и нажмите клавиатурную комбинацию (Ctrl+L), чтобы отобразить предполагаемый план выполнения.SELECT DISTINCT SalesOrderID, CarrierTrackingNumber FROM dbo.OrderDetails WHERE ProductID = 776

Если вы задержите указатель мыши над оператором IndexSeek, то увидите, что SQL Server выполняет поиск по индексу (метод seek) в таблице NCLIX_OrderDetails_ProductID, чтобы извлечь указатели на нужные записи. Поскольку в этой таблице существует seek ) по кластеризованному индексу, чтобы возвратить нужные строки данных, которые затем переходят к оператору Sort (Сортировка), чтобы исключить повторяющиеся значения в результате. Так SQL Server извлекает строки при помощи
DROP INDEX, чтобы удалить из таблицы OrderDetails DROP INDEX OrderDetails.CLIDX_OrderDetails
SELECT и нажмите клавиатурную комбинацию (Ctrl+L), чтобы отобразить предполагаемый план выполнения.SELECT DISTINCT SalesOrderID, CarrierTrackingNumber FROM dbo.OrderDetails WHERE ProductID = 776

Мы видим, что в этом случае SQL Server использует оператор RID Lookup, потому что указатели, которые SQL Server получил в результате поиска по индексу (метод seek), представляют собой указатели на физические строки данных, а не на ключи кластеризации. Оператор RID Lookup - это оператор, который используется в SQL Server для извлечения данных непосредственно со страницы.
CREATE UNIQUE CLUSTERED INDEX CLIDX_OrderDetails ON dbo.OrderDetails(SalesOrderID,SalesOrderDetailID)
Не всегда нужно, чтобы при использовании
Adventure Works.SalesOrderID, CarrierTrackingNumber и ProductID.NCLIX_OrderDetails_ProductID, который мы создали ранее, включает столбец ProductID, поскольку он построен на этом столбце, а также столбец SalesOrderID, поскольку этот столбец является ключевым столбцом SalesOrderID является указателем, который SQL Server использует в некластеризованном индексе. Следовательно, серверу SQL Server, чтобы получить CarrierTrackingNumber, нужно возвратить строки данных, выполнив поиск (метод seek) только по кластеризованному индексу. Во втором запросе столбца CarrierTrackingNumber нет в списке SELECT. Введите и выполните инструкцию, включив действительный план выполнения, чтобы увидеть разницу. Код этого примера имеется в файлах примеров под именем Using .
SET STATISTICS IO ON -не покрывающий SELECT DISTINCT SalesOrderID, CarrierTrackingNumber FROM dbo.OrderDetails WHERE ProductID = 776 -покрывающий SELECT DISTINCT SalesOrderID FROM dbo.OrderDetails WHERE ProductID = 776 SET STATISTICS IO OFF
На рисунке, показанном ниже, видно, что серверу SQL Server для выполнения второго запроса нужно обратиться к кластеризованному индексу, потому что индекс покрывает запрос, если столбец CarrierTrackingNumber не выбран. Поскольку доступ к кластеризованному индексу для каждой строки очень дорого стоит, второй запрос составляет только 1% от общей стоимости пакета. Посмотрев на вкладку Messages (Сообщения), мы видим, что для того,. чтобы покрыть запрос, SQL Server нужно выполнить только 2 считывания страницы (вместо 709 считываний, необходимых для первого запроса).

Вы убедились, что покрывающий индекс может дать большой выигрыш в скорости выполнения. Конечно, невозможно удалить столбцы из запроса, если они нужны в результате. Но по общему правилу следует извлекать только те столбцы, которые вам действительно нужны, чтобы сделать более вероятным применение покрывающего индекса. Не следует использовать в инструкции SELECT символ звездочки (*) только потому, что так проще составить запрос.
CarrierTrackingNumber в CREATE ) индекса с включенным столбцом.DROP INDEX dbo.OrderDetails.NCLIX_OrderDetails_ProductID CREATE INDEX NCLIX_OrderDetails_ProductID ON dbo.OrderDetails(ProductID) INCLUDE (CarrierTrackingNumber)
SET STATISTICS IO ON SELECT DISTINCT SalesOrderID, CarrierTrackingNumber FROM dbo.OrderDetails WHERE ProductID = 776 SET STATISTICS IO OFF
Взглянув на план выполнения, вы увидите, что доступ к кластеризованному индексу больше не нужен. На вкладке сообщений видно, что для выполнения этого запроса теперь требуется только 5 считываний страниц, тогда как раньше требовалось 709.
В таблице dbo.OrderDetails у нас есть вычисляемый столбец LineTotal, который представляет значение LineTotal для строки и вычисляется по следующей формуле:
(isnull(([UnitPrice]*((1.0)-[UnitPriceDiscount]))*[OrderQty],(0.0)))
При каждом доступе к этому столбцу SQL Server вычисляет значения на основе значений оригинальных строк, на которые ссылается формула. Этот процесс не представляет собой проблемы до тех пор, пока столбец LineTotal используется в предложении SELECT, но если использовать LineTotal в предикате поиска предложения WHERE или в агрегатных функциях, например, MAX или MIN, такая проблема может возникнуть. Если вычисляемые столбцы используются для поиска, SQL Server должен вычислять значения для каждой строки в таблице, и только потом искать в результатах нужные строки. Это очень неэффективный процесс, потому что здесь всегда требуется просмотр таблицы или полный просмотр
Adventure Works.SalesOrderID s, для которых LineTotal имеет определенную величину. Введите следующий запрос и нажмите (Ctrl+L), чтобы отобразить предполагаемый план выполнения для случая, когда в вычисляемом столбце не существует опорного индекса. Как видите, SQL Server должен выполнить просмотр IndexesOnComputedColumns.sql.SELECT SalesOrderID FROM OrderDetails WHERE LineTotal = 27893.619

CREATE INDEX, чтобы создать индекс в вычисляемом столбце:CREATE NONCLUSTERED INDEX NCL_OrderDetail_LineTotal ON dbo.OrderDetails(LineTotal)
SELECT SalesOrderID FROM OrderDetails WHERE LineTotal = 27893.619

SQL Server 2005 имеет собственный тип данных XML. Экземпляры XML в столбцах типа данных XML хранятся как большие двоичные объекты (BLOB) и могут иметь размер до 2 Гбайт на каждый экземпляр. Для запросов к XML-данным можно использовать язык XQuery, но такие запросы к столбцам с типом данных XML без индекса могут занимать много времени. Это особенно справедливо для больших экземпляров XML, поскольку SQL Server должен разбирать большие двоичные объекты, содержащие XML, в процессе выполнения
Первый индекс, который следует создать в столбце XML - это первичный XML-индекс. При создании этого индекса SQL Server разбирает XML-содержимое и создает несколько строк данных, которые включают такую информацию, как имя элемента и атрибута, путь к корневому узлу, тип узла и значения и т. д. Благодаря этой информации SQL Server будет гораздо проще поддерживать запросы XQuery. Чтобы создать первичный XML-индекс, в базовой таблице должен существовать первичный ключ с
CREATE PRIMARY XML INDEX index_name ON <object> ( xml_column_name )
Adventure Works.CreatingAndUsingPrimaryXMLIndexes.sql.CREATE TABLE dbo.Products( ProductID int NOT NULL,
Name dbo.Name NOT NULL, CatalogDescription xml NULL,
CONSTRAINT PK_ProductModel_ProductID PRIMARY KEY CLUSTERED ( ProductID ));
INSERT INTO dbo.Products (ProductID,Name,CatalogDescription)
SELECT ProductModelID, Name, CatalogDescription
FROM Production.ProductModel;
CREATE INDEX используется для создания первичного индекса в столбце описания каталога таблицы Production.ProductModel.CREATE PRIMARY XML INDEX PRXML_Products_CatalogDesc ON dbo.Products (CatalogDescription);
XQuery для извлечения XML- данных только в том случае, если в XML-документе существует указанный путь. Введите и выполните эту инструкцию и обязательно включите план выполнения запроса.WITH XMLNAMESPACES
("http://schemas.microsoft.com/sqlserver/2004/07/adventure-works /ProductModelDescription'
AS "PD")
SELECT ProductID, CatalogDescription
FROM dbo.Products
WHERE CatalogDescription.exist
("/PD:ProductDescription/PD:Features") = 1
Хотя первичные XML-индексы и повышают производительность запросов XQuery вследствие того, что XML-данные разбираются, SQL Server все же должен просматривать разобранные данные, чтобы найти среди них запрашиваемые данные. Чтобы еще больше повысить производительность запроса, можно создать поверх первичного XML-индекса вторичный XML-индекс. Существует три типа вторичных XML-индексов, и каждый тип поддерживает определенные типы запросов к XML-столбцам, предоставляя функции для создания только тех типов индексов, которые требуются для определенного сценария. Вот эти три типа вторичных XML-индексов:
.exist для определения существования указанного пути.Общая синтаксическая конструкция для создания вторичных индексов XML такова:
CREATE XML INDEX index name
ON <object> ( xml column name )
USING XML INDEX xml index name
FOR { VALUE | PATH | PROPERTY }
Adventure Works.path, value и property в столбце XML CatalogDescription. Код этого примера можно найти среди файлов примеров под именем CreatingAndUsingSecondaryXMLindexes.sql.CREATE XML INDEX IXML Products CatalogDesc Path ON dbo.Products (CatalogDescription) USING XML INDEX PRXML Products CatalogDesc FOR PATH CREATE XML INDEX IXML_Products_CatalogDesc_Value ON dbo.Products (CatalogDescription) USING XML INDEX PRXML_Products_CatalogDesc FOR VALUE CREATE XML INDEX IXML_Products_CatalogDesc_Property ON dbo.Products (CatalogDescription) USING XML INDEX PRXML_Products_CatalogDesc FOR PROPERTY
XQuery, и запросите предполагаемый план выполнения, нажав комбинацию клавиш (Ctrl+L). Рассмотрим различные способы использования только что созданных индексов сервером SQL Server.WITH XMLNAMESPACES
("http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/
ProductModelDescription' AS "PD") SELECT *
FROM dbo.Products
WHERE CatalogDescription.exist
("/PD:ProductDescription/@ProductModelID[.="19"]') = 1;
WITH XMLNAMESPACES
("http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/
ProductModelDescription' AS "PD")
SELECT *
FROM dbo.Products
WHERE CatalogDescription.exist ("//PD:*/@ProductModelID[.="19"]') = 1;
WITH XMLNAMESPACES
("http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/
ProductModelDescription' AS "PD")
SELECT CatalogDescription.value
("(/PD:ProductDescription/@ProductModelID)[1]",
"int") as PID
FROM dbo.Products
WHERE CatalogDescription.exist
("/PD:ProductDescription/@ProductModelID") = 1;
Представлению без индекса не требуется место для хранения; когда оно используется в инструкции, SQL Server объединяет описание представления с инструкцией, оптимизирует, генерирует план выполнения запроса и возвращает данные. Издержки этого процесса могут быть значительными, если представление обрабатывает или объединяет много строк. В такой ситуации может оказаться полезным индексировать представление, если к нему часто выполняются запросы.
При индексации представление обрабатывается, и результат хранится в файле данных, как это делается для кластеризованной таблицы. SQL Server автоматически обслуживает этот индекс при изменении данных в базовой таблице. Индексы в представлениях могут существенно ускорить доступ к данным через представления, но, безусловно, индексация приводит к дополнительным затратам ресурсов при изменении данных в базовой таблице. Следовательно, можно предусмотреть использование индексации представлений, если представление обрабатывает много строк, как при использовании агрегатных функций, а также если данные в базовой таблице не слишком часто изменяются.
В SQL Server 2005 Enterprise, Developer или Evaluation Edition индексированные представления могут ускорить выполнение запросов, которые не ссылаются на представления напрямую. Если обрабатываемый запрос включает, например, агрегат, и
Чтобы создать индексированное представление, выполните следующие действия:
SCHEMABINDING. Это представление должно удовлетворять нескольким требованиям. Например, оно может ссылаться только на базовые таблицы, которые существуют в одной базе данных. Все ссылочные функции должны быть детерминированными; функции наборов, производные таблицы и подзапросы не допустимы. Полный список требований можно найти в теме "Создание индексированных представлений" Электронной документации SQL Server 2005.Adventure Works.LineTotal, сгруппированный по месяцам заказа. Код этого примера можно найти среди файлов примеров под именем CreatingAndUsingIndexedViews.sql.CREATE VIEW dbo.vOrderDetails
WITH SCHEMABINDING AS
SELECT DATEPART(yy,Orderdate) as Year,
DATEPART(mm,Orderdate) as Month,
SUM(LineTotal) as OrderTotal,
COUNT_BIG(*) as LineCount
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
GROUP BY DATEPART(yy,Orderdate),
DATEPART(mm,Orderdate)
SELECT. На вкладке Messages (Сообщения) видно, что для выполнения этой инструкции SQL Server требуется почти 1000 считываний страниц.SET STATISTICS IO ON SELECT Year, Month, OrderTotal FROM dbo.OrderDetails ORDER BY Year, Month SET STATISTICS IO OFF
CREATE INDEX, чтобы создать уникальный vOrderDetails.CREATE UNIQUE CLUSTERED INDEX CLIDX_vOrderDetails_Year_Month ON dbo.vOrderDetails(Year,Month)
SELECT. Обратите внимание на то, что SQL Server требуется только два считывания страниц, потому что результат уже вычислен и хранится в индексе.SET STATISTICS IO ON SELECT Year, Month, OrderTotal FROM dbo.OrderDetails ORDER BY Year, Month SET STATISTICS IO OFF
SELECT, которая не ссылается на пред
ставление, и нажмите (Ctrl+L), чтобы запросить предполагаемый план
выполнения, как показано ниже.SELECT DATEPART(yy,Orderdate) as Year,
SUM(LineTotal) as YearTotal
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
GROUP BY DATEPART(yy,Orderdate)

YearTotal,
вычислив сумму найденных в представлении агрегатов за месяц. Таким образом, мы видим, что при наличии индексированных представлений становятся возможным ускорить запрос, не изменяя код самого запроса.Adventure Works.Examining Join Operations.sql. План выполнения этой инструкции показан ниже. Мы видим, что SQL Server для того, чтобы извлечь строки внутреннего ввода (данные из таблицы OrderDetails ), использует поиск по индексу (метод seek ), а затем оператор Nested Loop Join , потому что во внешнем вводе есть только одна совпадающая строка (данные из таблицы Orders ). Совпадающие строки внешнего ввода возвращаются также с помощью поиска по индексу (метод seek ), поскольку существует совпадающий индекс. Для извлечения данных SQL Server требуется только пять считываний страниц, как указано на вкладке Messages (Сообщения).SET STATISTICS IO ON
SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID = 43659

SalesOrderID. В этом случае SQL Server выполнит соединение слиянием, поскольку в индексе есть отсортированные строки, а во внутреннем вводе много строк. Введите и выполните следующий запрос: Вы увидите, что для выполнения этого запроса SQL Server требуется 19 считываний страниц.SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o
INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID BETWEEN 43659 AND 44000

DROP INDEX CLIDX_Orders_SalesOrderID ON dbo.Orders DROP INDEX CLIDX_OrderDetails ON dbo.OrderDetails
SELECT, которую мы использовали ранее (она снова показана ниже) и изучите изменения планов выполнения запросов и операций ввода/вывода.SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID = 43659
SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID BETWEEN 43659 AND 44000

План выполнения предыдущего запроса показывает, что SQL Server снова использует соединение вложенных циклов для первого запроса, потому что внутренний ввод небольшой, и хэшированное соединение для второго запроса, потому что данные ввода больше не сортируются. Поскольку опорных индексов не существует, SQL Server приходится сканировать в обоих случаях базовые таблицы полностью, что требует более 1000 считываний страниц для каждого запроса.
Как вы убедились прочитав этот раздел, для соединений очень важно иметь опорные индексы. По общему правилу все ограничения по внешнему ключу должны быть индексированы, потому что они являются условиями соединения почти для всех запросов.
В последнем примере мы видели, что SQL Sever выбирает разные
Таково поведение по умолчанию, но для большинства баз данных существует лучший вариант. Можно воспользоваться инструкцией ALTER DATABASE, чтобы информировать SQL Server о том, что нужно обновлять данные статистики асинхронно; это означает, что программа не будет ждать новых статистических данных при генерации плана выполнения запроса. Безусловно, это означает, что сгенерированный план выполнения запроса может не быть оптимальным, поскольку при его создании были использованы устаревшие статистические данные. В особых случаях может быть желательным сгенерировать или обновить данные статистики вручную. Это можно сделать с помощью инструкций CREATE STATISTICS или UPDATE STATISTICS. Можно также отключить автоматическое создание и обновление индекса на уровне базы данных, выполнив инструкцию ALTER DATABASE. Все эти варианты следует использовать только в особых ситуациях, потому что поведение по умолчанию прекрасно подходит для большинства случаев. Дополнительную информацию об этих вариантах можно прочитать в официальном документе "Statistics Used by the
Adventure Works.sys.stats и sys.stats_columns.Введите и выполните следующий запрос, чтобы узнать, какие статистические данные существуют для таблицы dbo:OrderDetails. Код этого примера можно найти среди файлов примеров под именем DataDistributionStatistics.sql.
SELECT s.NAME, COL_NAME ( s.object_ID, sc.column_id) as CNAME
FROM sys.stats s INNER JOIN sys.stats_columns sc
ON s.stats_id = sc.stats_id
AND s.object_id = sc.object_id
WHERE s.object_id = OBJECT_ID('dbo.OrderDetails')
ORDER BY s.NAME;"
Результат сообщает нам имена статистических показателей и столбцов, для которых они рассчитаны. Статистические данные для индексированных столбцов получают названия по названию соответствующего индекса. Статистические данные с именами, которые начинаются с WA_Sys - это статистика, которую SQL Server создает автоматически для столбцов, не имеющих индекса.
LineTotal таблицы dbo.OrderDetails, введите и выполните следующую инструкцию:DBCC SHOW_STATISTICS('dbo.OrderDetails', 'LineTotal')
Панель результатов, показанная ниже, отображает часть вывода инструкции DBCCSHOW_ STATISTICS.
Первая часть - это общая информация, например, дата создания, количество строк в таблице и количество строк в выборке.
Отображается также информация о плотности данных.
Плотность - это величина, показывающая, сколько неповторяющихся значений в анализируемом столбце.
Помимо этой общей информации SQL Server определяет диапазоны в данных, которые называются шагами, и сохраняет статистику распределения для этих шагов.
С помощью этой статистики распределения SQL Server может, используя статистическую информацию для диапазона,
к которому принадлежит указанное значение, предположить, сколько строк будет вовлечено в поиск указанного значения.
Для каждого шага SQL Server хранит следующую информацию:
RANGE_HI_KEY - значение верхней границы шага.EQ_ROWS - количество строк, которое равно значению the RANGE_HI_KEY.RANGE_ROWS - количество строк в диапазоне, не считая границ.DISTINCT_RANGE_ROWS - количество неповторяющихся значений внутри диапазона.При создании
Когда выполняется вставка данных в таблицу, эти данные сохраняются на указанной странице среди страниц уровня листовых вершин UPDATE и DELETE.
Чтобы уменьшить фрагментацию, можно использовать при создании индекса параметр FILLFACTOR, который определяет, до какого процента должны быть заполнены страницы уровня листовых вершин индекса, когда создаются индексы. При более низком значении параметра FILLFACTOR фрагментация наименее вероятна, потому что на странице уровня листовых вершин можно поместить большие записи без разбиения страницы. С другой стороны, для более низких значений параметра FILLFACTOR индекс будет больше, потому что изначально на каждой странице уровня листовых вершин хранится меньше данных.
Если индексируемая таблица не является таблицей только для чтения, то ее индексы рано или поздно будут фрагментированы. Фрагмен-тированные индексы можно дефрагментировать для повышения скорости доступа к данным при помощи инструкции ALTER INDEX. Для
REORGANIZE сортирует только страницы данных, а не записи на страницах; это означает, что параметр FILLFACTOR при реорганизации использовать нельзя.FILLFACTOR, чтобы страницы снова заполнялись до желаемой степени. Если параметр FILLFACTOR не указывается, то страницы уровня листовых вершин заполняются до предела. Параметр ONLINE также может указываться при перестройке индексов. Если этот параметр не указан, то перестройка индекса выполняется в автономном режиме, что означает блокировку таблицы на протяжении всего процесса. Перестройка в автономном режиме выполняется быстрее, чем в рабочем режиме, но из-за блокировки данных она не может использоваться в то время, когда необходим доступ к данным.Adventure Works.IndexFragmentation.sql.SELECT object_name(i.object_id) as object_name ,i.name as IndexName
,ps.avg_fragmentation_in_percent
,avg_page_space_used_in_percent
FROM sys.dm_db_index_physical_stats(db_id(), NULL, NULL, NULL , 'DETAILED') as ps
INNER JOIN sys.indexes as i
ON i.object_id = ps.object_id AND i.index_id = ps.index_id
WHERE ps.avg_fragmentation_in_percent > 50
AND ps.index_id > 0 ORDER BY 1
ALTER INDEX PK_Employee_EmployeeID ON HumanResources.Employee REBUILD WITH (ONLINE = ON)
Создание правильных индексов для проекта базы данных - непростая задача. Здесь необходимо учесть множество факторов:
Чтобы помочь пользователю в проектировании индексов, SQL Server предлагает инструмент, который называется
Adventure Works.UsingDatabaseEngineTuningAdvisor.sql.USE AdventureWorks;
SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID = 43659;
SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID BETWEEN 43659 AND 44000;
dta .sql.
Как мы убедились, SQL Server в меру своих возможностей пытается оптимизировать два запроса. Это выгодно только в том случае, если эти запросы должны оптимизироваться без учета эффекта этой оптимизации на другие операции базы данных. Чтобы оптимизировать все индексы базы данных, неплохой идеей будет использование трассировки SQL Server Profiler, которую предоставляет
В этой лекции рассказывалось о том, как SQL Server хранит данные и осуществляет к ним доступ с использованием индексов и без них. На основе анализа планов запросов и статистики операций ввода/вывода был сделан вывод о важности существования корректных индексов для оптимизации производительности. Кроме того, вы научились использовать различные типы индексов (которые для наглядности объединены в представленной ниже таблице) и обслуживать их.
| Типы индексов | |
|---|---|
| Хранит строки данных таблицы на уровне листовых индекс. вершин индексов. Предоставляет быструю сортировку и ранжирование доступа к данным на основе |
|
Обеспечивает быстрое выполнение операции поиска по индексу (метод seek ) на основе |
|
| Индекс вычисляемых столбцов | Хранит вычисляемые столбцы и обеспечивает быстрый доступ при использовании вычисляемого столбца в качестве аргумента поиска. |
| Индекс XML-столбца | Обеспечивает быстрый доступ к столбцам XML |
| Индексированное представление | Хранит результат представления и обеспечивает быстрый доступ к нему. Полезно, если к представлению часто выполняются запросы, особенно с использованием агрегатов. |
Из этой лекции вы узнали, как важно подобрать правильный тип индекса и как Помощник по настройке ядра СУБД может оказать помощь в разработке индекса.
| Чтобы | Выполните следующие действия |
|---|---|
| Просмотреть предполагаемый план выполнения запроса | Нажмите (Ctrl+L) или выберите команду Display |
| Просмотреть действительный план выполнения запроса | Нажмите (Ctrl+М) или выберите команду Include Actual Execution Plan (Включить действительный план выполнения) из меню Query (Запрос). Действительный план выполнения отображается на вкладке Execution Plan (План выполнения). |
| Создать кластеризованный индекс | CREATE UNIQUE CLUSTERED INDEX <index_name> ON <table>(<column>) |
| Создать |
CREATE [ UNIQUE ] NONCLUSTERED INDEX index_name ON <object> ( column [ ASC | DESC ] [ ,...n ] ) |
| Создать первичный XML-индекс | CREATE PRIMARY XML INDEX index_name ON <object> ( xml_column_name ) |
| Создать вторичный XML-индекс | CREATE XML INDEX index_name
ON <object> ( xml_column_name ) USING XML INDEX xml_index_name
FOR { VALUE | PATH | PROPERTY }
|
| Просмотреть распределение данных | Выполните запрос к представлениям sys.stats и sys.stats_columns. Для определенного столбца используйте инструкцию DBCC SHOW_STATISTICS(<table>, <column>) |
| Получить информацию о фрагментации индексов | Используйте функцию наборов sys.dm_db_physical_stats. |
| Перестроить индекс и выполнить |
ALTER INDEX <index>
ON <table>.<column>
REBUILD.
|
| Использовать |
В SQL Server Management Studio выберите из меню Tools (Сервис) команду |
В предыдущей лекции вы научились извлекать сводную информацию о данных, хранящихся в вашей базе данных. SQL Server может возвращать результаты, в том числе, сводную информацию, быстро и эффективно, если данные правильно хранятся в базе данных. В этой лекции объясняются различные способы хранения и извлечения данных в SQL Server, а также рассказывается о том, какие факторы следует учитывать при разработке базы данных, чтобы добиться наиболее эффективной производительности от SQL Server.
Когда сервер SQL Server выполняет запрос, сначала требуется определить наилучший способ выполнения. Для этого нужно рассчитать, как и в каком порядке обращаться к данным и соединять их, как и когда выполнять вычисления и агрегации и т. д. За это отвечает подсистема, которая называется
Сможет ли
Как видите, генерирование плана выполнения запросов - это функция, немаловажная для производительности SQL Server, поскольку эффективность плана выполнения запроса определяет, будет ли время его выполнения измеряться в миллисекундах, секундах или даже минутах. Планы выполнения запросов, которые показали низкую скорость выполнения, можно проанализировать, чтобы определить, имеется ли индекс, устарели ли данные статистики или просто SQL Server выбрал не самый эффективный план (такое случается не очень часто).
Adventure Works, выбрав ее из раскрывающегося списка Available Databases (Доступные базы данных).SELECT. Код этого примера имеется в файлах примеров под именем Viewing Query Plans .sql.SELECT SalesOrderID, OrderQTY FROM Sales.SalesOrderDetail WHERE ProductID = 712 ORDER BY OrderQTY DESC

При генерировании предполагаемого плана запроса запрос на самом деле не выполняется. Он только оптимизируется Оптимизатором запроса. Эта особенность Оптимизатора запросов является преимуществом, когда приходится иметь дело с запросами, которые имеют продолжительные
Sort (Сортировка), который сортирует данные на основе предложения ORDER BY.Мы рассмотрим самые важные операторы, которые использует SQL Server, когда будем изучать индексы и соединения. Полный список операторов можно найти в Электронной документации SQL Server 2005, тема "Пиктограммы графического представления плана выполнения".
Стоимость в процентах под пиктограммой каждого оператора показывает процент от общей стоимости запроса, представленного на графической схеме. Это число поможет вам понять, какая операция использует при выполнении больше всего ресурсов. В нашем случае самой дорогостоящей операцией является Clustered Index Scan (Просмотр
В этом окне отображается подробная информация об операции. До сих пор мы знали только то, что SQL Server извлекает данные при помощи операции сканирования. Но в этом окне видно, что он выполняет операцию Clustered Index Scan (Просмотр Sales.SalesOrderDetail, а также поиск ProductID 712. Эта информация находится в секции Predicates (Предикаты). Кроме того, показаны предполагаемая стоимость и предполагаемое количество строк, а также размер строки. В то время, как количество строк оценивается на основе статистики, которую SQL Server хранит для этой таблицы, значения стоимости вычисляются на основе статистики и значений эталонной системы. Следовательно, значения стоимости не следует использовать для того, чтобы рассчитать, сколько времени запрос будет выполняться на компьютере. Эти цифры могут использоваться только для выявления более дешевой или более дорогостоящей операции.
.sqlplan. Его можно открыть через SQL Server Management Studio. выбрав из меню File (Файл) команды Open, File (Открыть, Файл).Теперь мы рассмотрим, как работают индексы и как они повышают производительность запросов. В SQL Server 2005 можно определить два различных типа индексов: кластеризованные и некластеризованные. Чтобы понять, как индексы могут ускорить доступ к данным и какие типы индексов использовать в конкретных ситуациях, необходимо понимать, как данные и индексы хранятся в файлах данных и как SQL Server осуществляет доступ к данным в файлах данных.
Файл данных в базе данных SQL Server делится на страницы по 8 Кбайт. Каждая страница содержит данные, индексы или другие типы данных, которые нужны SQL Server, чтобы манипулировать файлами данных. Однако большая часть страниц - это страницы данных или индекса. Страницы представляют собой единицы, которые SQL Server считывает и записывает из файлов данных. Каждая страница содержит данные или информацию индекса только для одного объекта базы данных. Поэтому на каждой странице данных вы найдете данные об одном объекте, а на каждой странице индекса - только информацию индекса. В SQL Server 2000 невозможно разбить строки данных на несколько страниц; это означает, что строка данных должна уместиться на странице, что ограничивает размер строки примерно 8060 байт (за исключением больших объектов данных). В SQL Server 2005 это ограничение больше не существует для типов данных переменной длины, таких, как nvarchar, varbinary, CLR и т. п. Благодаря типам данных переменной длины строки могут занимать несколько страниц, но все строки с типом данных фиксированной длины все же должны вписываться в одну страницу.
Когда пользователь создает таблицу и вносит в нее данные, SQL Server осуществляет поиск неиспользуемых страниц, на которых можно сохранить данные. Чтобы отслеживать, какие страницы содержат данные для таблиц, SQL Server для каждой таблицы хранит еще одну или больше дополнительных страниц IAM, Index
Adventure Works.dbo.Orders и dbo.OrderDetails. Для создания таблиц и заполнения их данными ведите и выполните следующие инструкции. Код для всего примера можно найти среди файлов примеров под именем Examining Heap Structures.sql.USE AdventureWorks;
GO
CREATE TABLE dbo.Orders( SalesOrderID int NOT NULL,
OrderDate datetime NOT NULL,
ShipDate datetime NULL,
Status tinyint NOT NULL,
PurchaseOrderNumber dbo.OrderNumber NULL,
CustomerID int NOT NULL,
ContactID int NOT NULL,
SalesPersonID int NULL
);
CREATE TABLE dbo.OrderDetails( SalesOrderID int NOT NULL,
SalesOrderDetailID int NOT NULL,
CarrierTrackingNumber nvarchar(25),
OrderQty smallint NOT NULL,
ProductID int NOT NULL,
UnitPrice money NOT NULL,
UnitPriceDiscount money NOT NULL,
LineTotal AS (isnull((UnitPrice*((1.0)-UnitPriceDiscount))*OrderQty,(0.0)))
);
INSERT INTO dbo.Orders
SELECT SalesOrderID, OrderDate, ShipDate, Status, PurchaseOrderNumber,
CustomerID, ContactID, SalesPersonID
FROM Sales.SalesOrderHeader;
INSERT INTO dbo.OrderDetails(SalesOrderID, SalesOrderDetailID,
CarrierTrackingNumber, OrderQty,
ProductID, UnitPrice, UnitPriceDiscount)
SELECT SalesOrderID, SalesOrderDetailID,CarrierTrackingNumber,OrderQty,
ProductID, UnitPrice, UnitPriceDiscount
FROM Sales.SalesOrderDetail;
dbo.Orders введите инструкции, которые приводятся ниже. Включите действительный план выполнения, нажав (Ctrl+M) до начала выполнения или выбрав из меню Query (Запрос) команду Include Actual Execution Plan (Включить действительный план выполнения). Выполните запрос:SET STATISTICS IO ON; SELECT * FROM dbo.Orders SET STATISTICS IO OFF
Параметр SET STATISTICS IO включает функцию, которая вызывает отправку сообщений о выполненных операциях дискового ввода/ вывода обратно клиенту сервером SQL Server при выполнении инструкции. Это замечательная функция, которую следует использовать для определения стоимости операций ввода/вывода для запросов.

Этот вывод информирует, что SQL Server для данной операции должен просмотреть данные таблицы один раз, причем нужно выполнить 178 считываний страниц (логических чтений). Этот вывод показывает также, что физические считывания для выполнения этой операции не используются (физические, или опережающие считывания). Физических считываний не было потому, что, в данном случае, данные уже находились в буферном кэше. Если окно Messages (Сообщения) показывает, что в данном запросе выполнялись физические считывания, то выполните запрос еще раз; вы увидите, что количество физических считываний будет меньше, чем было до этого. Причина заключается в том, что SQL Server хранит страницы данных, к которым недавно были обращения, в буферном кэше для повышения производительности.

SET STATISTICS IO ON; SELECT * FROM dbo.Orders WHERE SalesOrderID =46699; SET STATISTICS IO OFF;
Чтобы повысить производительность доступа к данным, следует определить индексы в столбцах. Столбец, в котором задан индекс, называется ключевым столбцом индекса. Индекс, встроенный в столбец, похож на оглавление книги. Он включает отсортированные значения из этого столбца и ссылки на страницы, на которых можно найти действительные строки и данные.
Чтобы найти строки, в которых столбец индекса имеет определенное значение, SQL Server должен будет найти в индексе это значение, а затем перейти по указателю, чтобы прочитать строку. Эта операция намного проще и дешевле, чем просмотр всех данных, который был выполнен нами при выполнении просмотра таблицы.
Индексы в SQL Server встроены в древовидную структуру, которая называется сбалансированным деревом. Основная структура сбалансированного дерева показана ниже в примере. Как видите, нижний уровень называется уровнем листовых вершин. Уровень листовых вершин можно мыслить как оглавление книги. Он содержит по одной записи на каждую строку данных, причем записи сортируются по столбцу индекса. Чтобы ускорить поиск значений в индексе, дерево построено поверх него с использованием операций сравнения < (меньше чем) и > (больше чем). Число уровней индекса зависит от количества записей и размера ключа индекса. В реальных условиях страница индекса могла бы содержать намного больше значений, чем изображено в примере. Поскольку страница имеет размер 8 Кбайт, SQL Server может указать на странице индекса на тысячи страниц. Следовательно, индекс обычно не имеет много уровней, даже если таблица содержит миллионы строк. Этот факт способствует очень быстрому поиску определенных значений.
Надписи: Root (Level 2) - Уровень корневой вершины (2 уровень) Intermediate (Level 1) - Уровень внутренних вершин (1 уровень) Leaf Level (Level 0) - Уровень листовых вершин (0 уровень) KEY Pointer - Ключ-указатель
Как уже отмечалось ранее, в SQL Server используется два типа индексов: кластеризованный и некластеризованный. Оба типа индексов представляют собой сбалансированные деревья, но построены они по-разному. Давайте посмотрим, в чем заключается разница.
Поскольку данные включены в
CREATE [ UNIQUE ] CLUSTERED INDEX index name ON <object> ( column [ ASC | DESC ] [ ,...n ] )
Как видите, можно определить индекс как уникальный, в этом случае одинаковые значения ключа индекса в двух строках недопустимы.
CREATE или ALTER TABLE с ключевыми словами CLUSTERED или NONCLUSTERED.Adventure Works.Orders ведите и выполните следующую инструкцию. Код этого примера имеется в файлах примеров под именем Creating And Using Clustered Indexes.sql.CREATE UNIQUE CLUSTERED INDEX CLIDX_Orders_SalesOrderID
ON dbo.Orders(SalesOrderID)
SELECT, как делали это раньше, и изучите различия. Обязательно включите при выполнении запроса действительный план выполнения.SET STATISTICS IO ON; SELECT * FROM dbo.Orders; SELECT * FROM dbo.Orders WHERE SalesOrderID =46699; SET STATISTICS IO OFF;

Мы видим, что SQL Server больше не использует просмотры таблицы. Теперь он выполняет операции индексирования, потому что данные больше не хранятся в структуре кучи. План выполнения запроса показывает, что в данном случае используются две основные операции с индексами из всех возможных:
SELECT не имеет предложения WHERE, серверу SQL Server известно, что нужно возвратить все данные, которые хранятся на уровне листовых вершин индекса.Эти две операции могут также соединяться, чтобы извлечь определенный диапазон данных. В этом виде операции частичного просмотра SQL Server пытается найти начало диапазона, а затем просматривает дерево до конца этого диапазона.

Мы видим, что первая инструкция SELECT генерирует почти тот же объем считываний страниц, что и операция просмотра таблицы, если у нее структура кучи. Это не удивительно, поскольку инструкция SELECT требует все данные, и, следовательно, SQL Server должен возвратить все данные. Но второй запрос генерирует только два считывания страниц, а это значительное улучшение по сравнению с теми 178 считываниями страниц, которые были показаны раньше. SQL Server необходимо только выполнить поиск по индексу, что требует гораздо меньшего количества операций ввода/вывода, чем поиск в каждой странице данных.
SELECT, которая возвращает данные в отсортированной форме, и нажмите (Ctrl+L), чтобы получить предполагаемый план выполнения.SELECT * FROM dbo.Orders ORDER BY SalesOrderID; SELECT * FROM dbo.Orders ORDER BY OrderDate;

Из рисунка видно, что первая инструкция выполняет только просмотр SalesOrderID, потому что этот столбец является ключом
Во втором запросе данные были отсортированы после получения. Следовательно, после операции просмотра Sort (Сортировка), которая отсортировала данные по столбцу OrderDate. Поскольку сортировка является очень дорогостоящей операцией, второй запрос генерирует 93% общей стоимости обоих запросов. Таким образом, имеет смысл определять
Теперь создадим составной индекс в таблице OrderDetails. Составной индекс - это индекс, который определен более, чем для одного столбца.
OrderDetails.CREATE UNIQUE CLUSTERED INDEX CLIDX_OrderDetails ON dbo.OrderDetails(SalesOrderID,SalesOrderDetailID)
SELECT. Первая будет выполнять поиск указанного значения SalesOrderID, а вторая -указанного значения SalesOrderDetailID.
Оба столбца являются индексными столбцами нашего индекса CLIDX_OrderDetails.
Нажмите (Ctrl+L), чтобы отобразить предполагаемый план выполнения.SELECT * FROM dbo.OrderDetails WHERE SalesOrderID = 46999 SELECT * FROM dbo.OrderDetails WHERE SalesOrderDetailID = 14147
Легко заметить, что в первом запросе, который выполняет поиск значения из первого столбца составного индекса, SQL Server для поиска строки использует поиск по индексам (метод seek). Во втором запросе он использует просмотр индекса, который является более дорогостоящей операцией. Просмотр индекса используется потому, что невозможно искать значение только во втором столбце составного индекса, ведь индекс изначально сортируется по первому столбцу. Следовательно, важно продумать порядок индексных столбцов в составном индексе. Запомните, что составной индекс следует использовать только тогда, когда поиск по дополнительным столбцам выполняется исключительно в сочетании с первым столбцом или если должна быть применена уникальность.
В противоположность кластеризованным индексам
Поскольку
CREATE [ UNIQUE ] NONCLUSTERED INDEX index name
ON <object> ( column [ ASC | DESC ] [ ,...n ] )
Adventure Works.SELECT и нажмите клавиатурную комбинацию (Ctrl+L), чтобы отобразить предполагаемый план выполнения. Код этого примера имеется в файлах примеров под именем Creating And Using Nonclustered Indexes.sql.SELECT DISTINCT SalesOrderID, CarrierTrackingNumber FROM dbo.OrderDetails WHERE ProductID = 776
SQL Server выполняет операцию просмотра OrderDetails нет индекса. Чтобы ускорить выполнение этого запроса, SQL Server потребуется индекс в столбце ProductID. Поскольку OrderDetails уже определен, приходится использовать
Sort (Сортировка) применяется в этом плане выполнения запроса для получения неповторяющегося результата.Product ID таблицы OrderDetail, введите и выполните следующую инструкцию:CREATE INDEX NCLIX_OrderDetails_ProductID ON dbo.OrderDetails(ProductID)
SELECT и нажмите клавиатурную комбинацию (Ctrl+L), чтобы отобразить предполагаемый план выполнения.SELECT DISTINCT SalesOrderID, CarrierTrackingNumber FROM dbo.OrderDetails WHERE ProductID = 776

Если вы задержите указатель мыши над оператором IndexSeek, то увидите, что SQL Server выполняет поиск по индексу (метод seek) в таблице NCLIX_OrderDetails_ProductID, чтобы извлечь указатели на нужные записи. Поскольку в этой таблице существует seek ) по кластеризованному индексу, чтобы возвратить нужные строки данных, которые затем переходят к оператору Sort (Сортировка), чтобы исключить повторяющиеся значения в результате. Так SQL Server извлекает строки при помощи
DROP INDEX, чтобы удалить из таблицы OrderDetails DROP INDEX OrderDetails.CLIDX_OrderDetails
SELECT и нажмите клавиатурную комбинацию (Ctrl+L), чтобы отобразить предполагаемый план выполнения.SELECT DISTINCT SalesOrderID, CarrierTrackingNumber FROM dbo.OrderDetails WHERE ProductID = 776

Мы видим, что в этом случае SQL Server использует оператор RID Lookup, потому что указатели, которые SQL Server получил в результате поиска по индексу (метод seek), представляют собой указатели на физические строки данных, а не на ключи кластеризации. Оператор RID Lookup - это оператор, который используется в SQL Server для извлечения данных непосредственно со страницы.
CREATE UNIQUE CLUSTERED INDEX CLIDX_OrderDetails ON dbo.OrderDetails(SalesOrderID,SalesOrderDetailID)
Не всегда нужно, чтобы при использовании
Adventure Works.SalesOrderID, CarrierTrackingNumber и ProductID.NCLIX_OrderDetails_ProductID, который мы создали ранее, включает столбец ProductID, поскольку он построен на этом столбце, а также столбец SalesOrderID, поскольку этот столбец является ключевым столбцом SalesOrderID является указателем, который SQL Server использует в некластеризованном индексе. Следовательно, серверу SQL Server, чтобы получить CarrierTrackingNumber, нужно возвратить строки данных, выполнив поиск (метод seek) только по кластеризованному индексу. Во втором запросе столбца CarrierTrackingNumber нет в списке SELECT. Введите и выполните инструкцию, включив действительный план выполнения, чтобы увидеть разницу. Код этого примера имеется в файлах примеров под именем Using .
SET STATISTICS IO ON -не покрывающий SELECT DISTINCT SalesOrderID, CarrierTrackingNumber FROM dbo.OrderDetails WHERE ProductID = 776 -покрывающий SELECT DISTINCT SalesOrderID FROM dbo.OrderDetails WHERE ProductID = 776 SET STATISTICS IO OFF
На рисунке, показанном ниже, видно, что серверу SQL Server для выполнения второго запроса нужно обратиться к кластеризованному индексу, потому что индекс покрывает запрос, если столбец CarrierTrackingNumber не выбран. Поскольку доступ к кластеризованному индексу для каждой строки очень дорого стоит, второй запрос составляет только 1% от общей стоимости пакета. Посмотрев на вкладку Messages (Сообщения), мы видим, что для того,. чтобы покрыть запрос, SQL Server нужно выполнить только 2 считывания страницы (вместо 709 считываний, необходимых для первого запроса).

Вы убедились, что покрывающий индекс может дать большой выигрыш в скорости выполнения. Конечно, невозможно удалить столбцы из запроса, если они нужны в результате. Но по общему правилу следует извлекать только те столбцы, которые вам действительно нужны, чтобы сделать более вероятным применение покрывающего индекса. Не следует использовать в инструкции SELECT символ звездочки (*) только потому, что так проще составить запрос.
CarrierTrackingNumber в CREATE ) индекса с включенным столбцом.DROP INDEX dbo.OrderDetails.NCLIX_OrderDetails_ProductID CREATE INDEX NCLIX_OrderDetails_ProductID ON dbo.OrderDetails(ProductID) INCLUDE (CarrierTrackingNumber)
SET STATISTICS IO ON SELECT DISTINCT SalesOrderID, CarrierTrackingNumber FROM dbo.OrderDetails WHERE ProductID = 776 SET STATISTICS IO OFF
Взглянув на план выполнения, вы увидите, что доступ к кластеризованному индексу больше не нужен. На вкладке сообщений видно, что для выполнения этого запроса теперь требуется только 5 считываний страниц, тогда как раньше требовалось 709.
В таблице dbo.OrderDetails у нас есть вычисляемый столбец LineTotal, который представляет значение LineTotal для строки и вычисляется по следующей формуле:
(isnull(([UnitPrice]*((1.0)-[UnitPriceDiscount]))*[OrderQty],(0.0)))
При каждом доступе к этому столбцу SQL Server вычисляет значения на основе значений оригинальных строк, на которые ссылается формула. Этот процесс не представляет собой проблемы до тех пор, пока столбец LineTotal используется в предложении SELECT, но если использовать LineTotal в предикате поиска предложения WHERE или в агрегатных функциях, например, MAX или MIN, такая проблема может возникнуть. Если вычисляемые столбцы используются для поиска, SQL Server должен вычислять значения для каждой строки в таблице, и только потом искать в результатах нужные строки. Это очень неэффективный процесс, потому что здесь всегда требуется просмотр таблицы или полный просмотр
Adventure Works.SalesOrderID s, для которых LineTotal имеет определенную величину. Введите следующий запрос и нажмите (Ctrl+L), чтобы отобразить предполагаемый план выполнения для случая, когда в вычисляемом столбце не существует опорного индекса. Как видите, SQL Server должен выполнить просмотр IndexesOnComputedColumns.sql.SELECT SalesOrderID FROM OrderDetails WHERE LineTotal = 27893.619

CREATE INDEX, чтобы создать индекс в вычисляемом столбце:CREATE NONCLUSTERED INDEX NCL_OrderDetail_LineTotal ON dbo.OrderDetails(LineTotal)
SELECT SalesOrderID FROM OrderDetails WHERE LineTotal = 27893.619

SQL Server 2005 имеет собственный тип данных XML. Экземпляры XML в столбцах типа данных XML хранятся как большие двоичные объекты (BLOB) и могут иметь размер до 2 Гбайт на каждый экземпляр. Для запросов к XML-данным можно использовать язык XQuery, но такие запросы к столбцам с типом данных XML без индекса могут занимать много времени. Это особенно справедливо для больших экземпляров XML, поскольку SQL Server должен разбирать большие двоичные объекты, содержащие XML, в процессе выполнения
Первый индекс, который следует создать в столбце XML - это первичный XML-индекс. При создании этого индекса SQL Server разбирает XML-содержимое и создает несколько строк данных, которые включают такую информацию, как имя элемента и атрибута, путь к корневому узлу, тип узла и значения и т. д. Благодаря этой информации SQL Server будет гораздо проще поддерживать запросы XQuery. Чтобы создать первичный XML-индекс, в базовой таблице должен существовать первичный ключ с
CREATE PRIMARY XML INDEX index_name ON <object> ( xml_column_name )
Adventure Works.CreatingAndUsingPrimaryXMLIndexes.sql.CREATE TABLE dbo.Products( ProductID int NOT NULL,
Name dbo.Name NOT NULL, CatalogDescription xml NULL,
CONSTRAINT PK_ProductModel_ProductID PRIMARY KEY CLUSTERED ( ProductID ));
INSERT INTO dbo.Products (ProductID,Name,CatalogDescription)
SELECT ProductModelID, Name, CatalogDescription
FROM Production.ProductModel;
CREATE INDEX используется для создания первичного индекса в столбце описания каталога таблицы Production.ProductModel.CREATE PRIMARY XML INDEX PRXML_Products_CatalogDesc ON dbo.Products (CatalogDescription);
XQuery для извлечения XML- данных только в том случае, если в XML-документе существует указанный путь. Введите и выполните эту инструкцию и обязательно включите план выполнения запроса.WITH XMLNAMESPACES
("http://schemas.microsoft.com/sqlserver/2004/07/adventure-works /ProductModelDescription'
AS "PD")
SELECT ProductID, CatalogDescription
FROM dbo.Products
WHERE CatalogDescription.exist
("/PD:ProductDescription/PD:Features") = 1
Хотя первичные XML-индексы и повышают производительность запросов XQuery вследствие того, что XML-данные разбираются, SQL Server все же должен просматривать разобранные данные, чтобы найти среди них запрашиваемые данные. Чтобы еще больше повысить производительность запроса, можно создать поверх первичного XML-индекса вторичный XML-индекс. Существует три типа вторичных XML-индексов, и каждый тип поддерживает определенные типы запросов к XML-столбцам, предоставляя функции для создания только тех типов индексов, которые требуются для определенного сценария. Вот эти три типа вторичных XML-индексов:
.exist для определения существования указанного пути.Общая синтаксическая конструкция для создания вторичных индексов XML такова:
CREATE XML INDEX index name
ON <object> ( xml column name )
USING XML INDEX xml index name
FOR { VALUE | PATH | PROPERTY }
Adventure Works.path, value и property в столбце XML CatalogDescription. Код этого примера можно найти среди файлов примеров под именем CreatingAndUsingSecondaryXMLindexes.sql.CREATE XML INDEX IXML Products CatalogDesc Path ON dbo.Products (CatalogDescription) USING XML INDEX PRXML Products CatalogDesc FOR PATH CREATE XML INDEX IXML_Products_CatalogDesc_Value ON dbo.Products (CatalogDescription) USING XML INDEX PRXML_Products_CatalogDesc FOR VALUE CREATE XML INDEX IXML_Products_CatalogDesc_Property ON dbo.Products (CatalogDescription) USING XML INDEX PRXML_Products_CatalogDesc FOR PROPERTY
XQuery, и запросите предполагаемый план выполнения, нажав комбинацию клавиш (Ctrl+L). Рассмотрим различные способы использования только что созданных индексов сервером SQL Server.WITH XMLNAMESPACES
("http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/
ProductModelDescription' AS "PD") SELECT *
FROM dbo.Products
WHERE CatalogDescription.exist
("/PD:ProductDescription/@ProductModelID[.="19"]') = 1;
WITH XMLNAMESPACES
("http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/
ProductModelDescription' AS "PD")
SELECT *
FROM dbo.Products
WHERE CatalogDescription.exist ("//PD:*/@ProductModelID[.="19"]') = 1;
WITH XMLNAMESPACES
("http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/
ProductModelDescription' AS "PD")
SELECT CatalogDescription.value
("(/PD:ProductDescription/@ProductModelID)[1]",
"int") as PID
FROM dbo.Products
WHERE CatalogDescription.exist
("/PD:ProductDescription/@ProductModelID") = 1;
Представлению без индекса не требуется место для хранения; когда оно используется в инструкции, SQL Server объединяет описание представления с инструкцией, оптимизирует, генерирует план выполнения запроса и возвращает данные. Издержки этого процесса могут быть значительными, если представление обрабатывает или объединяет много строк. В такой ситуации может оказаться полезным индексировать представление, если к нему часто выполняются запросы.
При индексации представление обрабатывается, и результат хранится в файле данных, как это делается для кластеризованной таблицы. SQL Server автоматически обслуживает этот индекс при изменении данных в базовой таблице. Индексы в представлениях могут существенно ускорить доступ к данным через представления, но, безусловно, индексация приводит к дополнительным затратам ресурсов при изменении данных в базовой таблице. Следовательно, можно предусмотреть использование индексации представлений, если представление обрабатывает много строк, как при использовании агрегатных функций, а также если данные в базовой таблице не слишком часто изменяются.
В SQL Server 2005 Enterprise, Developer или Evaluation Edition индексированные представления могут ускорить выполнение запросов, которые не ссылаются на представления напрямую. Если обрабатываемый запрос включает, например, агрегат, и
Чтобы создать индексированное представление, выполните следующие действия:
SCHEMABINDING. Это представление должно удовлетворять нескольким требованиям. Например, оно может ссылаться только на базовые таблицы, которые существуют в одной базе данных. Все ссылочные функции должны быть детерминированными; функции наборов, производные таблицы и подзапросы не допустимы. Полный список требований можно найти в теме "Создание индексированных представлений" Электронной документации SQL Server 2005.Adventure Works.LineTotal, сгруппированный по месяцам заказа. Код этого примера можно найти среди файлов примеров под именем CreatingAndUsingIndexedViews.sql.CREATE VIEW dbo.vOrderDetails
WITH SCHEMABINDING AS
SELECT DATEPART(yy,Orderdate) as Year,
DATEPART(mm,Orderdate) as Month,
SUM(LineTotal) as OrderTotal,
COUNT_BIG(*) as LineCount
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
GROUP BY DATEPART(yy,Orderdate),
DATEPART(mm,Orderdate)
SELECT. На вкладке Messages (Сообщения) видно, что для выполнения этой инструкции SQL Server требуется почти 1000 считываний страниц.SET STATISTICS IO ON SELECT Year, Month, OrderTotal FROM dbo.OrderDetails ORDER BY Year, Month SET STATISTICS IO OFF
CREATE INDEX, чтобы создать уникальный vOrderDetails.CREATE UNIQUE CLUSTERED INDEX CLIDX_vOrderDetails_Year_Month ON dbo.vOrderDetails(Year,Month)
SELECT. Обратите внимание на то, что SQL Server требуется только два считывания страниц, потому что результат уже вычислен и хранится в индексе.SET STATISTICS IO ON SELECT Year, Month, OrderTotal FROM dbo.OrderDetails ORDER BY Year, Month SET STATISTICS IO OFF
SELECT, которая не ссылается на пред
ставление, и нажмите (Ctrl+L), чтобы запросить предполагаемый план
выполнения, как показано ниже.SELECT DATEPART(yy,Orderdate) as Year,
SUM(LineTotal) as YearTotal
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
GROUP BY DATEPART(yy,Orderdate)

YearTotal,
вычислив сумму найденных в представлении агрегатов за месяц. Таким образом, мы видим, что при наличии индексированных представлений становятся возможным ускорить запрос, не изменяя код самого запроса.Adventure Works.Examining Join Operations.sql. План выполнения этой инструкции показан ниже. Мы видим, что SQL Server для того, чтобы извлечь строки внутреннего ввода (данные из таблицы OrderDetails ), использует поиск по индексу (метод seek ), а затем оператор Nested Loop Join , потому что во внешнем вводе есть только одна совпадающая строка (данные из таблицы Orders ). Совпадающие строки внешнего ввода возвращаются также с помощью поиска по индексу (метод seek ), поскольку существует совпадающий индекс. Для извлечения данных SQL Server требуется только пять считываний страниц, как указано на вкладке Messages (Сообщения).SET STATISTICS IO ON
SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID = 43659

SalesOrderID. В этом случае SQL Server выполнит соединение слиянием, поскольку в индексе есть отсортированные строки, а во внутреннем вводе много строк. Введите и выполните следующий запрос: Вы увидите, что для выполнения этого запроса SQL Server требуется 19 считываний страниц.SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o
INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID BETWEEN 43659 AND 44000

DROP INDEX CLIDX_Orders_SalesOrderID ON dbo.Orders DROP INDEX CLIDX_OrderDetails ON dbo.OrderDetails
SELECT, которую мы использовали ранее (она снова показана ниже) и изучите изменения планов выполнения запросов и операций ввода/вывода.SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID = 43659
SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID BETWEEN 43659 AND 44000

План выполнения предыдущего запроса показывает, что SQL Server снова использует соединение вложенных циклов для первого запроса, потому что внутренний ввод небольшой, и хэшированное соединение для второго запроса, потому что данные ввода больше не сортируются. Поскольку опорных индексов не существует, SQL Server приходится сканировать в обоих случаях базовые таблицы полностью, что требует более 1000 считываний страниц для каждого запроса.
Как вы убедились прочитав этот раздел, для соединений очень важно иметь опорные индексы. По общему правилу все ограничения по внешнему ключу должны быть индексированы, потому что они являются условиями соединения почти для всех запросов.
В последнем примере мы видели, что SQL Sever выбирает разные
Таково поведение по умолчанию, но для большинства баз данных существует лучший вариант. Можно воспользоваться инструкцией ALTER DATABASE, чтобы информировать SQL Server о том, что нужно обновлять данные статистики асинхронно; это означает, что программа не будет ждать новых статистических данных при генерации плана выполнения запроса. Безусловно, это означает, что сгенерированный план выполнения запроса может не быть оптимальным, поскольку при его создании были использованы устаревшие статистические данные. В особых случаях может быть желательным сгенерировать или обновить данные статистики вручную. Это можно сделать с помощью инструкций CREATE STATISTICS или UPDATE STATISTICS. Можно также отключить автоматическое создание и обновление индекса на уровне базы данных, выполнив инструкцию ALTER DATABASE. Все эти варианты следует использовать только в особых ситуациях, потому что поведение по умолчанию прекрасно подходит для большинства случаев. Дополнительную информацию об этих вариантах можно прочитать в официальном документе "Statistics Used by the
Adventure Works.sys.stats и sys.stats_columns.Введите и выполните следующий запрос, чтобы узнать, какие статистические данные существуют для таблицы dbo:OrderDetails. Код этого примера можно найти среди файлов примеров под именем DataDistributionStatistics.sql.
SELECT s.NAME, COL_NAME ( s.object_ID, sc.column_id) as CNAME
FROM sys.stats s INNER JOIN sys.stats_columns sc
ON s.stats_id = sc.stats_id
AND s.object_id = sc.object_id
WHERE s.object_id = OBJECT_ID('dbo.OrderDetails')
ORDER BY s.NAME;"
Результат сообщает нам имена статистических показателей и столбцов, для которых они рассчитаны. Статистические данные для индексированных столбцов получают названия по названию соответствующего индекса. Статистические данные с именами, которые начинаются с WA_Sys - это статистика, которую SQL Server создает автоматически для столбцов, не имеющих индекса.
LineTotal таблицы dbo.OrderDetails, введите и выполните следующую инструкцию:DBCC SHOW_STATISTICS('dbo.OrderDetails', 'LineTotal')
Панель результатов, показанная ниже, отображает часть вывода инструкции DBCCSHOW_ STATISTICS.
Первая часть - это общая информация, например, дата создания, количество строк в таблице и количество строк в выборке.
Отображается также информация о плотности данных.
Плотность - это величина, показывающая, сколько неповторяющихся значений в анализируемом столбце.
Помимо этой общей информации SQL Server определяет диапазоны в данных, которые называются шагами, и сохраняет статистику распределения для этих шагов.
С помощью этой статистики распределения SQL Server может, используя статистическую информацию для диапазона,
к которому принадлежит указанное значение, предположить, сколько строк будет вовлечено в поиск указанного значения.
Для каждого шага SQL Server хранит следующую информацию:
RANGE_HI_KEY - значение верхней границы шага.EQ_ROWS - количество строк, которое равно значению the RANGE_HI_KEY.RANGE_ROWS - количество строк в диапазоне, не считая границ.DISTINCT_RANGE_ROWS - количество неповторяющихся значений внутри диапазона.При создании
Когда выполняется вставка данных в таблицу, эти данные сохраняются на указанной странице среди страниц уровня листовых вершин UPDATE и DELETE.
Чтобы уменьшить фрагментацию, можно использовать при создании индекса параметр FILLFACTOR, который определяет, до какого процента должны быть заполнены страницы уровня листовых вершин индекса, когда создаются индексы. При более низком значении параметра FILLFACTOR фрагментация наименее вероятна, потому что на странице уровня листовых вершин можно поместить большие записи без разбиения страницы. С другой стороны, для более низких значений параметра FILLFACTOR индекс будет больше, потому что изначально на каждой странице уровня листовых вершин хранится меньше данных.
Если индексируемая таблица не является таблицей только для чтения, то ее индексы рано или поздно будут фрагментированы. Фрагмен-тированные индексы можно дефрагментировать для повышения скорости доступа к данным при помощи инструкции ALTER INDEX. Для
REORGANIZE сортирует только страницы данных, а не записи на страницах; это означает, что параметр FILLFACTOR при реорганизации использовать нельзя.FILLFACTOR, чтобы страницы снова заполнялись до желаемой степени. Если параметр FILLFACTOR не указывается, то страницы уровня листовых вершин заполняются до предела. Параметр ONLINE также может указываться при перестройке индексов. Если этот параметр не указан, то перестройка индекса выполняется в автономном режиме, что означает блокировку таблицы на протяжении всего процесса. Перестройка в автономном режиме выполняется быстрее, чем в рабочем режиме, но из-за блокировки данных она не может использоваться в то время, когда необходим доступ к данным.Adventure Works.IndexFragmentation.sql.SELECT object_name(i.object_id) as object_name ,i.name as IndexName
,ps.avg_fragmentation_in_percent
,avg_page_space_used_in_percent
FROM sys.dm_db_index_physical_stats(db_id(), NULL, NULL, NULL , 'DETAILED') as ps
INNER JOIN sys.indexes as i
ON i.object_id = ps.object_id AND i.index_id = ps.index_id
WHERE ps.avg_fragmentation_in_percent > 50
AND ps.index_id > 0 ORDER BY 1
ALTER INDEX PK_Employee_EmployeeID ON HumanResources.Employee REBUILD WITH (ONLINE = ON)
Создание правильных индексов для проекта базы данных - непростая задача. Здесь необходимо учесть множество факторов:
Чтобы помочь пользователю в проектировании индексов, SQL Server предлагает инструмент, который называется
Adventure Works.UsingDatabaseEngineTuningAdvisor.sql.USE AdventureWorks;
SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID = 43659;
SELECT o.SalesOrderID, o.OrderDate, od.ProductID
FROM dbo.Orders o INNER JOIN dbo.OrderDetails od
ON o.SalesOrderID = od.SalesOrderID
WHERE o.SalesOrderID BETWEEN 43659 AND 44000;
dta .sql.
Как мы убедились, SQL Server в меру своих возможностей пытается оптимизировать два запроса. Это выгодно только в том случае, если эти запросы должны оптимизироваться без учета эффекта этой оптимизации на другие операции базы данных. Чтобы оптимизировать все индексы базы данных, неплохой идеей будет использование трассировки SQL Server Profiler, которую предоставляет
В этой лекции рассказывалось о том, как SQL Server хранит данные и осуществляет к ним доступ с использованием индексов и без них. На основе анализа планов запросов и статистики операций ввода/вывода был сделан вывод о важности существования корректных индексов для оптимизации производительности. Кроме того, вы научились использовать различные типы индексов (которые для наглядности объединены в представленной ниже таблице) и обслуживать их.
| Типы индексов | |
|---|---|
| Хранит строки данных таблицы на уровне листовых индекс. вершин индексов. Предоставляет быструю сортировку и ранжирование доступа к данным на основе |
|
Обеспечивает быстрое выполнение операции поиска по индексу (метод seek ) на основе |
|
| Индекс вычисляемых столбцов | Хранит вычисляемые столбцы и обеспечивает быстрый доступ при использовании вычисляемого столбца в качестве аргумента поиска. |
| Индекс XML-столбца | Обеспечивает быстрый доступ к столбцам XML |
| Индексированное представление | Хранит результат представления и обеспечивает быстрый доступ к нему. Полезно, если к представлению часто выполняются запросы, особенно с использованием агрегатов. |
Из этой лекции вы узнали, как важно подобрать правильный тип индекса и как Помощник по настройке ядра СУБД может оказать помощь в разработке индекса.
| Чтобы | Выполните следующие действия |
|---|---|
| Просмотреть предполагаемый план выполнения запроса | Нажмите (Ctrl+L) или выберите команду Display |
| Просмотреть действительный план выполнения запроса | Нажмите (Ctrl+М) или выберите команду Include Actual Execution Plan (Включить действительный план выполнения) из меню Query (Запрос). Действительный план выполнения отображается на вкладке Execution Plan (План выполнения). |
| Создать кластеризованный индекс | CREATE UNIQUE CLUSTERED INDEX <index_name> ON <table>(<column>) |
| Создать |
CREATE [ UNIQUE ] NONCLUSTERED INDEX index_name ON <object> ( column [ ASC | DESC ] [ ,...n ] ) |
| Создать первичный XML-индекс | CREATE PRIMARY XML INDEX index_name ON <object> ( xml_column_name ) |
| Создать вторичный XML-индекс | CREATE XML INDEX index_name
ON <object> ( xml_column_name ) USING XML INDEX xml_index_name
FOR { VALUE | PATH | PROPERTY }
|
| Просмотреть распределение данных | Выполните запрос к представлениям sys.stats и sys.stats_columns. Для определенного столбца используйте инструкцию DBCC SHOW_STATISTICS(<table>, <column>) |
| Получить информацию о фрагментации индексов | Используйте функцию наборов sys.dm_db_physical_stats. |
| Перестроить индекс и выполнить |
ALTER INDEX <index>
ON <table>.<column>
REBUILD.
|
| Использовать |
В SQL Server Management Studio выберите из меню Tools (Сервис) команду |
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.