Я не собирался писать эту статью. Точнее, когда сегодня утром я сел за компьютер, у меня не было намерения работать над заявленной в заголовке темой. Но в конце концов я все же взялся за нее. Дело в том, что в процессе работы над статьей о случаях некорректного использования ключевого слова DISTINCT я сделал неожиданное открытие, присмотревшись к демонстрационному коду, который составлял для этого текста. Рассмотрим две ситуации.

Вариант 1: несогласованные типы данных

Рассмотрим две таблицы, приведенные на экранах 1 и 2, которые используются в базе данных коллекции метаданных SQL. В каждой таблице обратите внимание на столбец Server (который, кстати, в обеих таблицах выступает в роли первичного ключа). Вы заметите, что в dbo.ServerList_SSIS, табличном листинге, с помощью которого серверы собирают метаданные, эта часть таблицы выведена как столбец nvarchar(256). В таблице, используемой для хранения собранных экземпляров метаданных, dbo.SQL_Servers, упомянутый компонент выводится как столбец varchar(100). Если бы нам нужно было выполнить следующий запрос с целью возвращения информации из dbo.SQL_Servers для любой базы данных, выделенной для сбора и упомянутой в качестве экземпляра SQL 2000, мы бы увидели план выполнения кода листинга 1, а также статистику ввода-вывода (см. рисунки 1 и 2).

 

Таблица 1
Экран 1. Таблица 1

 

Таблица 2
Экран 2. Таблица 2

 

Результаты выполнения запроса
Рисунок 1. Результаты выполнения запроса

 

План выполнения запроса с совпадающими типами данных
Рисунок 2. План выполнения запроса с совпадающими типами данных

Вариант 2: совпадающие типы данных

Что же произойдет, если мы приведем тип данных, содержащихся в столбце Server таблицы dbo.Serverlist_SSIS, к типу данных, представленных в столбце Server таблицы SQL_Servers так, чтобы получить в обоих случаях данные типа varchar(100)? Я создал дублирующую базу данных и соответствующим образом изменил тип содержащихся в ней данных (см. экраны 3 и 4).

 

Экран 3. Измененная таблица 1
Экран 3. Измененная таблица 1

 

Измененная таблица 2
Экран 4. Измененная таблица 2

Теперь если мы с использованием этих таблиц выполним тот же запрос в листинге 2, план в результате получится совсем другой, см. рисунок 3.

 

План выполнения запроса
Рисунок 3. План выполнения запроса с совпадающими типами данных

Мы не только преобразовали результаты сканирования кластеризованного индекса базы dbo.SQL_Servers, выполняя поиск, но и обошлись без двух операций сортировки, поскольку в итоге выполнения каждой операции, затрагивающей две таблицы, были получены упорядоченные результаты, которые оставались упорядоченными при осуществлении вложенной операции цикла. Статистические показатели ввода-вывода тоже оказались гораздо лучше: логических операций считывания потребовалось всего 22 вместо 49, то есть в два раза меньше — и это далеко не все (см. рисунок 4).

 

Результаты выполнения измененного запроса
Рисунок 4. Результаты выполнения измененного запроса

Значение предварительного планирования схемы

Мы имели дело с небольшим запросом, а теперь представьте, какие преимущества можно получить от использования единообразных типов данных при работе с более объемными таблицами и наборами результатов. Решения, принимаемые на первых этапах разработки базы данных, исключительно важны и результаты их ощущаются на протяжении длительного времени. Выделите какое-то время для анализа типов данных, которые будете собирать, и приведите их к требуемому типу, а в дальнейшем будьте последовательны в выполнении своих решений по типам данных.

В рассмотренном выше примере нам повезло: изменение типов данных и выравнивание столбцов по объектам прошли без осложнений, нам не пришлось даже отбрасывать разряды в процессе округления.

Рассматривая причины низкого быстродействия той или иной структуры, помните о том, что дело не всегда в «неуклюжести кода». При отладке системы обязательно обращайте внимание на структуру объектов базы данных. Возможно, результаты этой работы станут для вас сюрпризом.

Листинг 1. Пример запроса

--Несовместимые типы
--(приводит к эксплицитной конверсии данных типов nvarchar(256) и varchar(100))
SELECT SS_BAD.Server
, SS_BAD.ProductVersion
, SS_BAD.ProductLevel
, SS_BAD.EngineEdition
FROM lifeboat_BAD.dbo.sql_servers AS SS_BAD
INNER JOIN lifeboat_BAD.dbo.serverlist_SSIS AS SSIS_BAD
ON SS_BAD.Server = SSIS_BAD.Server
WHERE SSIS_BAD.CONNECT = 1
AND SSIS_BAD.version = 8
ORDER BY SS_BAD.Server;

Листинг 2. Запрос с совпадающими типами данных

--Совпадающие типы данных
SELECT SS_GOOD.Server
, SS_GOOD.ProductVersion
, SS_GOOD.ProductLevel
, SS_GOOD.EngineEdition
FROM lifeboat_copy.dbo.sql_servers AS SS_GOOD
INNER JOIN lifeboat_copy.dbo.serverlist_SSIS AS SSIS_GOOD
ON SS_GOOD.Server = SSIS_GOOD.Server
WHERE SSIS_GOOD.CONNECT = 1
AND SSIS_GOOD.version = 8
ORDER BY SS_GOOD.Server;