Я не собирался писать эту статью. Точнее, когда сегодня утром я сел за компьютер, у меня не было намерения работать над заявленной в заголовке темой. Но в конце концов я все же взялся за нее. Дело в том, что в процессе работы над статьей о случаях некорректного использования ключевого слова 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 |
Экран 2. Таблица 2 |
Рисунок 1. Результаты выполнения запроса |
Рисунок 2. План выполнения запроса с совпадающими типами данных |
Вариант 2: совпадающие типы данных
Что же произойдет, если мы приведем тип данных, содержащихся в столбце Server таблицы dbo.Serverlist_SSIS, к типу данных, представленных в столбце Server таблицы SQL_Servers так, чтобы получить в обоих случаях данные типа varchar(100)? Я создал дублирующую базу данных и соответствующим образом изменил тип содержащихся в ней данных (см. экраны 3 и 4).
Экран 3. Измененная таблица 1 |
Экран 4. Измененная таблица 2 |
Теперь если мы с использованием этих таблиц выполним тот же запрос в листинге 2, план в результате получится совсем другой, см. рисунок 3.
Рисунок 3. План выполнения запроса с совпадающими типами данных |
Мы не только преобразовали результаты сканирования кластеризованного индекса базы dbo.SQL_Servers, выполняя поиск, но и обошлись без двух операций сортировки, поскольку в итоге выполнения каждой операции, затрагивающей две таблицы, были получены упорядоченные результаты, которые оставались упорядоченными при осуществлении вложенной операции цикла. Статистические показатели ввода-вывода тоже оказались гораздо лучше: логических операций считывания потребовалось всего 22 вместо 49, то есть в два раза меньше — и это далеко не все (см. рисунок 4).
Рисунок 4. Результаты выполнения измененного запроса |
Значение предварительного планирования схемы
Мы имели дело с небольшим запросом, а теперь представьте, какие преимущества можно получить от использования единообразных типов данных при работе с более объемными таблицами и наборами результатов. Решения, принимаемые на первых этапах разработки базы данных, исключительно важны и результаты их ощущаются на протяжении длительного времени. Выделите какое-то время для анализа типов данных, которые будете собирать, и приведите их к требуемому типу, а в дальнейшем будьте последовательны в выполнении своих решений по типам данных.
В рассмотренном выше примере нам повезло: изменение типов данных и выравнивание столбцов по объектам прошли без осложнений, нам не пришлось даже отбрасывать разряды в процессе округления.
Рассматривая причины низкого быстродействия той или иной структуры, помните о том, что дело не всегда в «неуклюжести кода». При отладке системы обязательно обращайте внимание на структуру объектов базы данных. Возможно, результаты этой работы станут для вас сюрпризом.
--Несовместимые типы --(приводит к эксплицитной конверсии данных типов 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;