Массивы в SQL Server 2000 (исходники)Источник: SQL Exercises
Иногда приходится сожалеть о том, что в Transact-SQL Microsoft SQL Server нет такой структуры как Array (массив). Многие программисты SQL поддержат меня. Массив - одна из наиболее общих и часто используемых структур программирования. Действительно, T-SQL не предусматривает полностью функциональную структуру массива. Однако SQL 2000 ввел новую возможность, называемую переменной табличного типа, которая позволяет имитировать массив и/или использоваться вместо курсора в SQL Server. В этой статье я детально остановлюсь на том, как можно использовать такой "массив" в хранимых процедурах, что в значительной степени расширяет функциональные возможности программирования на языке SQL. Я разбил свой подход на три части. 1. Создание массива в хранимых процедурах. Мой метод, прежде всего, основан на использовании переменных табличного типа, которые стали доступным, начиная с SQL Server 2000. Если Вы все еще работаете в версиях SQL Server 6.5 или 7, части 1 и 3 этой статьи могут все таки оказаться полезными для Вас. Однако Вы должны будете использовать временную таблицу (с префиксом #) вместо табличной переменной. Часть 2 посвящена использованию XML, что является причиной, по которой этот подход окажется бесполезным в любой среде разработки, предшествующей SQL 2000. ВТ (временные таблицы) также могут использоваться в версии 2000, однако используя временную таблицу, Вы увеличиваете взаимодействие с дисками, так как временные таблицы создаются базе данных TempDb в противоположность ТП (табличным переменным), создаваемым в памяти. Другими словами, хранимые процедуры, оперирующие временными таблицами медленнее тех, которые используют ТП. Для того чтобы SQL Server создал ВТ, система должна 1. Создать таблицу в системной базе данных TempDb Подобный процесс требуется и для ТП, но все операции проводятся 100-процентно в памяти. Я недавно экспериментировал с этими вариантами, и выяснил, что та же самая хранимая процедура, использующая ТП, выполнялась вдвое быстрей по сравнению с хранимой процедурой, которая использовала временные таблицы. Чтобы смоделировать массив (ТП), мы нуждаемся в структуре, подобной array, известной в большинстве сред программирования. Объявление табличной переменной выглядит следующим образом: Declare @tbl table ( ) Данный оператор создает эквивалент array. Тип этого "массива" зависит от типа данных столбца. Например, Declare @tbl table (FirstName varchar(20), LastName varchar(20)) создает массив строкового типа (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 * from @tbl [Как видно, в этом примере столбец RowID обеспечивает функциональность индекса, а ItemName хранит фактические значения.] Очевидно, что ТП является структурой программирования, эквивалентной МАССИВУ. Мы продемонстрировали создание массива в SQL Server программным образом. Наша главная задача состоит в том, чтобы использовать массив в хранимых процедурах, включая передачу ТП в качестве аргумента. Наряду с этим, я хотел бы детально остановиться еще на одной возможности передачи всего списка значений в хранимую процедуру через аргумент и использования его внутри процедуры. Для этого я использую XML. Однако эта структура стала доступной, только начиная с версии 2000 SQL Server. Когда требуется подобная операция? Например, если Вы намереваетесь передать в процедуру более одного значения, но не знаете заранее, сколько всего значений будут передаваться. Рассмотрим запрос: Select * from customers WHERE Region IN ('OR','CA','WA') Оператор IN в предложении WHERE может использовать переменное число значений и возвращает результирующий набор в соответствии с этим множественным критерием. Передача такого списка значений в хранимую процедуру может стать проблемой. В прежние времена я использовал так называемые "составные запросы", или "динамический SQL". Подход заключается в построении строки запроса, которая затем компилируется и выполняется при помощи оператора EXECUTE. Например: CREATE PROCEDURE CustomerByRegion set @sql = 'select * from Customers where Region IN (' + @List + ')' execute (@sql) -- Вызов процедуры Этот метод имеет некоторые ограничения. Переменная типа Varchar не может содержать более чем 8000 символов, а переменная типа Text/nText недопустима для объявления локальных переменных. Давайте рассмотрим сценарий, когда XML используется, чтобы передать список значений в хранимую процедуру. Из этого списка мы можем СОЗДАТЬ 'МАССИВ' (ТП), а затем использовать этот Массив, в данном случае в операторе IN - части предложения WHERE оператора SQL. Create Procedure ShowOrders Рассмотрим блок вставки более подробно. Мы наполнили массив, используя строку XML. Операторы OPENXML и OPENXML WITH создают представление набора строк (ROWSET VIEW ), используя документ XML. Полученный результат может быть загружен непосредственно в ТП или ВТ. Оператор WITH позволяет использовать существующее имя таблицы или создание новой схемы. Я рекомендовал бы второй вариант, так как это улучшает удобочитаемость и обслуживание. Кроме того, если Вы используете предложение WHERE, первый вариант может генерировать ошибку. -- Вариант 1 Теперь после передачи строк XML в ТП, мы можем использовать полученный результат в предложении IN. USE NorthWind CREATE PROC GetRegions_XML DECLARE @hDoc int exec sp_xml_preparedocument @hDoc OUTPUT, @empdata INSERT @tbl EXEC sp_xml_removedocument @hDoc SELECT * FROM Suppliers /* Использование курсоровКаждый знает о том, что курсоры съедают память. Именно поэтому рекомендуют использовать их как последнее средство, когда нет никаких других способов решить проблему. Это справедливо. Прошло довольно времени с тех пор, как я последний раз использовал курсор в моей практике программирования на T-SQL. И это понятно, т.к. с тех пор появилась намного лучшая альтернатива: использование ТП, которая великолепно работает и не так расточительна к ресурсам. Одна из основных причин использования курсора - функциональные возможности, которые позволяют работать с каждой отдельной строкой из данного результирующего набора. При кодировании подобной функциональной возможности с помощью ТП-массива нам потребуется идентификатор строки или ID для каждой строки. Использование свойства Identity очень удобно для этого. Свойство Identity может служить уникальным идентификатором для каждой строки и позволит сымитировать функциональность Fetch для построчной обработки. Ниже приведена сравнительная таблица для двух хранимых процедур, одна из которых использует традиционный курсор, и вторая - ТП. Вы можете обратить внимание, что хранимая процедура на основе курсора содержит меньше строк кода. Однако деятельность и ресурсы, используемые SQL Server, намного выше по сравнению с использованием ТП. Действительно, объявление курсора и использование операторов Fetch является компактным и прямым. В случае использования ТП, определенно используется больше кода, но это дает программисту больше возможностей по управлению и экономит ресурсы SQL Server и процессорное время, что, безусловно, является очень важной проблемой для крупных транзакционных приложений с большим числом пользователей. -- Курсор Transact-SQL -- Имитатор курсора Несмотря на тот факт, что имеется больше способов решить проблему, методы, которые я предложил, очевидно, расширяют наши возможности в использовании новых структур программирования SQL Server 2000. Alex Grinberg (оригинал: The ARRAY In SQL Server 2000) |