Новости сайта "Упражнения по SQL (http://www.sql-ex.ru)" Выпуск 46 (30 июля 2005 г.)
Новым посетителям сайта
Сайт посвящен изучению языка, с помощью которого осуществляется взаимодействие с реляционными (и не только) СУБД. Суть обучения состоит в выполнении заданий на написание запросов к учебным базам данных; при этом система контролирует правильность выполнения заданий. В настоящее время реализованы все операторы подъязыка манипуляции данными (DML), которые включают в себя оператор извлечения данных SELECT, а также операторы модификации данных - INSERT, DELETE и UPDATE.
Мы надеемся, что справочного материала сайта окажется достаточно для самостоятельного обучения. Кроме того, свои решения вы можете обсудить на форуме сайта. Опытных же специалистов приглашаем проверить (продемонстрировать) свое мастерство и принять участие в соревновании, обеспечиваемом рейтинговой системой учета времени выполнения заданий. Фактически, рейтинг ведется на втором этапе тестирования, который начинается сейчас после решения 59-ти задач первого этапа. При подсчете рейтинга каждого участника отбрасывается
один самый худший показатель среди всех решенных им упражнений.
Имеется возможность получить сертификат по SQL DML при выполнении определенного количества заданий.
Новости сайта
§ Появилась страница кандидатов на попадание в ТОР 100. На странице ТОР 100 имеется ссылка.
§ Привел, наконец, в соответствие рейтинговые позиции, выводящиеся в рейтинге за неделю.
§ По сообщению Shark добавил проверочных данных под задачу 104.
§ Замеченный Putin'ым прокол в проверке задачи 82 подлатал.
§ Число подписчиков - 2265
Иногда приходится сожалеть о том, что в Transact-SQL Microsoft SQL Server нет такой структуры как Array (массив). Многие программисты SQL поддержат меня. Массив - одна из наиболее общих и часто используемых структур программирования. Действительно, T-SQL не предусматривает полностью функциональную структуру массива. Однако SQL 2000 ввел новую возможность, называемую переменной табличного типа, которая позволяет имитировать массив и/или использоваться вместо курсора в SQL Server.
В этой статье я детально остановлюсь на том, как можно использовать такой "массив" в хранимых процедурах, что в значительной степени расширяет функциональные возможности программирования на языке SQL.
Я разбил свой подход на три части.
1. Создание массива в хранимых процедурах. 2. Передача и возвращение табличных переменных в/из хранимых процедур. 3. Использование 'массива' вместо курсоров.
Мой метод, прежде всего, основан на использовании переменных табличного типа, которые стали доступным, начиная с SQL Server 2000. Если Вы все еще работаете в версиях SQL Server 6.5 или 7, части 1 и 3 этой статьи могут все таки оказаться полезными для Вас. Однако Вы должны будете использовать временную таблицу (с префиксом #) вместо табличной переменной.
Часть 2 посвящена использованию XML, что является причиной, по которой этот подход окажется бесполезным в любой среде разработки, предшествующей SQL 2000. ВТ (временные таблицы) также могут использоваться в версии 2000, однако используя временную таблицу, Вы увеличиваете взаимодействие с дисками, так как временные таблицы создаются базе данных TempDb в противоположность ТП (табличным переменным), создаваемым в памяти. Другими словами, хранимые процедуры, оперирующие временными таблицами медленнее тех, которые
используют ТП. Для того чтобы SQL Server создал ВТ, система должна
1. Создать таблицу в системной базе данных TempDb 2. Ввести данные в таблицу (insert) 3. Извлечь данные (select) 4. Удалить таблицу (drop)
Подобный процесс требуется и для ТП, но все операции проводятся 100-процентно в памяти. Я недавно экспериментировал с этими вариантами, и выяснил, что та же самая хранимая процедура, использующая ТП, выполнялась вдвое быстрей по сравнению с хранимой процедурой, которая использовала временные таблицы.
Чтобы смоделировать массив (ТП), мы нуждаемся в структуре, подобной array, известной в большинстве сред программирования. Объявление табличной переменной выглядит следующим образом:
Declare @tbl table ( )
Данный оператор создает эквивалент array. Тип этого "массива" зависит от типа данных столбца. Например,
создает массив строкового типа (string). Другие типы данных, такие как int, float, datetime, money и т.д., также могут использоваться. Нужно помнить, что SQL Server не имеет функциональности (подобной VB и некоторым другим средам программирования) по неявному преобразованию типов данных. Это означает, что данные, которые Вы намереваетесь использовать для заполнения массива, должны быть отформатированы согласно спецификации каждого объявленного типа данных. Соответственно могут использоваться функции Convert
и Cast для форматирования данных.
Чтобы иметь "совершенный" массив с полными функциональными возможностями, у нас отсутствует индекс. Чтобы восполнить этот недостаток, я использую дополнительный столбец с типом данных Int и свойством Identity. Определение Identity требует 2 аргумента: начальное значение и приращение. Таким образом, мы можем создать массив с отсчетом от 0 или 1. Следующий массив обладает полными функциональными возможностями:
Declare @tbl table (RowId int identity(1,1), ItemName varchar(100))
Теперь мы можем заполнить этот массив (используя базу данных Northwind) следующим образом:
Insert @tbl Select ProductName FROM Products WHERE ProductName like 'M%'
Select * from @tbl GO -- Результат: RowId ItemName --------------------------------------- 1 Mishi Kobe Niku 2 Mascarpone Fabioli 3
Maxilaku 4 Manjimup Dried Apples 5 Mozzarella di Giovanni
[Как видно, в этом примере столбец RowID обеспечивает функциональность индекса, а ItemName хранит фактические значения.]
Очевидно, что ТП является структурой программирования, эквивалентной МАССИВУ.
Мы продемонстрировали создание массива в SQL Server программным образом. Наша главная задача состоит в том, чтобы использовать массив в хранимых процедурах, включая передачу ТП в качестве аргумента. Наряду с этим, я хотел бы детально остановиться еще на одной возможности передачи всего списка значений в хранимую процедуру через аргумент и использования его внутри процедуры. Для этого я использую XML. Однако эта структура стала доступной, только начиная с версии 2000 SQL Server. Когда требуется подобная операция?
Например, если Вы намереваетесь передать в процедуру более одного значения, но не знаете заранее, сколько всего значений будут передаваться. Рассмотрим запрос:
Select * from customers WHERE Region IN ('OR','CA','WA')
Оператор IN в предложении WHERE может использовать переменное число значений и возвращает результирующий набор в соответствии с этим множественным критерием.
Передача такого списка значений в хранимую процедуру может стать проблемой. В прежние времена я использовал так называемые "составные запросы", или "динамический SQL". Подход заключается в построении строки запроса, которая затем компилируется и выполняется при помощи оператора EXECUTE. Например:
CREATE PROCEDURE CustomerByRegion @List varchar(100) AS declare @sql varchar(1000)
set @sql = 'select * from Customers where Region IN (' + @List + ')'
Этот метод имеет некоторые ограничения. Переменная типа Varchar не может содержать более чем 8000 символов, а переменная типа Text/nText недопустима для объявления локальных переменных. Давайте рассмотрим сценарий, когда XML используется, чтобы передать список значений в хранимую процедуру. Из этого списка мы можем СОЗДАТЬ 'МАССИВ' (ТП), а затем использовать этот Массив, в данном случае в операторе IN - части предложения WHERE оператора SQL.
Create Procedure ShowOrders @XMLData
text /* тип данных TEXT позволяет нам принимать большую строку по сравнению с типом Varchar, который имеет ограничение в 8000 символов. */ AS Set Nocount ON Declare @hDoe int /* число, необходимое системе, чтобы распознать документ XML */ Declare @tbl table (ProductName varchar(100)) /* объявление ТП ('Array') */ Exec sp_xml_preparedocument @hDoc Output, @XMLdata /* Подготовка строки XML для дальнейшей обработки */ Insert @tbl select XML_Emp.ProductName From OPENXML(@hdoc,'root/products')
With Products XML_Emp
Рассмотрим блок вставки более подробно. Мы наполнили массив, используя строку XML. Операторы OPENXML и OPENXML WITH создают представление набора строк (ROWSET VIEW ), используя документ XML. Полученный результат может быть загружен непосредственно в ТП или ВТ. Оператор WITH позволяет использовать существующее имя таблицы или создание новой схемы. Я рекомендовал бы второй вариант, так как это улучшает удобочитаемость и обслуживание. Кроме того, если Вы используете предложение WHERE, первый вариант может генерировать
ошибку.
-- Вариант 1 Insert @tbl select XML_Emp.ProductName From OPENXML(@hdoc,'root/products') With Products XML_Emp -- Вариант 2: Insert @tbl select ProductName From OPENXML(@hdoc,'root/products') With (ProductName VARCHAR(20))
(окончание следует...)
Полезная информация
Конкурс
§ Мы выставили наш сайт на конкурс Интернить 2005. Победитель определяется числом поданых голосов. Просьба проголосовать. (рекомендуемая оценка 3 :-)).
Контакты
По всем вопросам, связанным с функционированием сайта, проблемами при решении упражнений, идеями вы можете обращаться к Сергею И.Моисеенко msi77@yandex.ru. Вы также можете предложить свои задачи для публикации на сайте.