(Возврат на основную страницу)
Editorial
В начале сентября Microsoft объявила о доступности бетаверсии WinFS — новой файловой системы, устанавливающей связь между традиционными файловыми системами и неструктурированными данными, такими как сообщения электронной почты и традиционные базы данных.
Конечно, мне известно, что такое WinFS. Ее история восходит к раннему периоду существования SQL Server 2000, когда Microsoft впервые стала рассуждать о том, что тогда называлось SQLFS. Можно также утверждать, что концептуально WinFS восходит к первым дням возникновения Windows NT (т. е. 1994 г.), когда Microsoft планировала релизобновление под кодовым названием Cairo (так никогда и не выпущенный), в состав которого предполагалось ввести объектноориентированную файловую систему. Было также заявлено, что WinFS войдет в Longhorn, но Microsoft исключила его из планов в прошлом году. Подобно многим другим, я считал, что Microsoft низвела WinFS до уровня «vaporware»1 (1 Саркастическое именование программного или аппаратного изделия, которое анонсировалось и рекламировалось, но так и не появилось. От «vapor» — «пар», «дым», «пустое место», «пускать пыль в глаза».) на неопределенный срок.
Льщу себя надеждой, что у меня немного больше информации, чем у среднего пользователя SQL Server, так что вам понятно мое удивление, когда я неожиданно узнал, что WinFS Beta 1 доступна для скачивания подписчикам MSDN и в итоге будет выпущена версия, работающая под Vista2 ( 2 То, что раньше называлось Longhorn.), и, вероятно, для Windows XP. (Первая бетаверсия будет работать под управлением Windows XP Service Pack 2, но Microsoft не утверждает, что эта возможность сохранится и в финальной версии продукта.) В результате я преисполнился надеждой, что WinFS принесет нам принципиально новую возможность работать с неструктурированными данными. Однако Microsoft говорит, что к моменту выпуска Vista WinFS еще не выйдет из стадии бетатестирования, — обычная песня «продукт выйдет, когда будет готов».
Следует отметить, что WinFS — это не просто еще одна галочка в длинном списке возможностей. Это продукт, действительно способный изменить то, как мы работаем и управляем огромными объемами информации, не уместившейся в традиционной базе данных. В своем блоге Шишир Мехротра (Shishir Mehrotra), руководитель группы планирования WinFS в Microsoft, приводит анекдот о том, как мы можем применять WinFS в реальной жизни. Полный текст вы сможете найти по адресу http://lists.sqlmag.com/t?ctl=13160:1036D. Мехротра остроумно рассказывает, как WinFS использует построитель запросов и информационную модель для представления почтового ящика сотрудника в виде реляционной базы данных, что позволяет более качественно управлять корреспонденцией. Этот блог описывает WinFS как «реляционную файловую систему», позволяющую приложению хранить нечто большее, чем просто типичный файл.
Если вам интересно узнать побольше о WinFS, обратитесь на специализированный сайт MSDN WinFS по адресу http://msdn.microsoft.com/data/winfs/default.aspx. Подписчики могут скачать бетаверсию WinFS. Указанная выше ссылка также позволяет перейти к очень интересным ресурсам, среди которых:
• подробные интервью с группой разработки WinFS;
• блог группы разработки WinFS;
• группа новостей WinFS.
Очевидно, что WinFS создает потенциал для целого класса новых приложений, которые было бы невозможно построить на пустом месте, не пользуясь основанием — платформой, предоставляемой Microsoft.
Programming
На публичных форумах, посвященных SQL Server, часто можно видеть людей, спрашивающих, как использовать массивы в SQL Server. Или они интересуются, почему не работает команда SELECT * FROM tbl WHERE col IN (@list). В этой статье рассматриваются несколько методов, позволяющих сделать это, — как хороших, так и плохих. Я также представлю данные об их производительности.
Если вас пугает размер этой статьи, знайте, что ее можно читать с любого места. Если вы простой программист SQL, который хочет узнать, как это сделать, то раздел о производительности может показаться слишком сложным. С другой стороны, настоящие фанаты SQL, которых интересуют результаты производительности, могут посчитать описания методов нудными, и им стоит пропустить эту часть.
Проблема
У вас есть несколько ключевых значений, определяющих пару строк в таблице, и вы хотите получить эти строки. Если вы пишете команду SELECT в клиентском коде, то код может быть таким:
SQL = "SELECT ProductID, ProductName
Ã
FROM Northwind..Products " & _
"WHERE ProductID IN (" & List & ")"
rs = cmd.Execute(SQL)
Здесь List — переменная, которой присвоена строка со списком разделенных запятыми значений, например «9, 12, 27, 39».
Если вы хотите использовать хранимые процедуры, то очевидного пути их применения нет. Некоторые делают так:
CREATE PROCEDURE get_product_names
Ã
@ids varchar(50) AS
SELECT ProductID, ProductName
FROM Northwind..Products
WHERE ProductID IN (@ids)
А потом пытаются вызвать:
EXEC get_product_names '9, 12, 27, 37'
Но получают ошибку:
Server: Msg 245, Level 16, State 1,
Ã
Procedure get_product_names, Line 2
Syntax error converting the varchar value Ã
'9, 12, 27, 37' to a column of data type int.
Эта ошибка возникает изза того, что мы больше не пишем команду SQL динамически, и @ids — это одно значение в выражении IN. В выражении IN можно также указать:
... WHERE col IN (@a, @b, @c)
Другие, столкнувшиеся с этой трудностью, говорят себе: если в TSQL есть массивы, как в любых нормальных языках, то это не проблема. Да, в TSQL действительно есть массивы. Они называются таблицами, и со всех сторон являются гораздо более универсальными, чем массивы. Если вам нужен всего лишь обычный массив с целочисленным индексом и одним значением в каждой ячейке, то он легко эмулируется таблицей. Однако ее нельзя передавать в хранимую процедуру как параметр. Когда одна хранимая процедура вызывает другую, это не является особой проблемой, потому что они могут использовать общую таблицу для обмена данными. Но это невозможно, когда вы вызываете хранимую процедуру из клиента. Правда, клиент способен передавать строку, и в SQL можно преобразовать ее в таблицу. Большинство способов, описанных в этой статье, используют этот принцип.
Обзор решений
Существует несколько возможных решений этой проблемы, которые могут использоваться в более или менее общем случае. Я представляю знакомые мне методы. Ниже показан достойный внимания список, разделенный на две группы.
Хорошие решения
• Итеративный метод. Движение по разделенному запятыми списку значений и возвращение элементов в таблицу реализуется либо пользовательской функцией (useddefined function, UDF), либо хранимой процедурой. Неплохая производительность, метод легок для понимания и решение легко расширяемо.
• Использование таблицы с числами для обработки разделенного запятыми списка значений либо в пользовательской функции, либо в хранимой процедуре. Самый быстрый из всех методов для списка с разделителями.
• Массив фиксированной длины. Вместо разделенного запятыми списка значений используйте строку, где все элементы имеют одинаковую длину. Строка обрабатывается таблицей с числами в пользовательской функции или хранимой процедуре. Это самый быстрый метод из всех, что я знаю.
• XMLXML. Значительно проигрывает на разделенном запятыми списке значений, но наилучшим образом подходит для массива структурированных данных. Использование возможно только в SQL 2000. Неплохая производительность, но меньшая, чем у итеративного метода.
Решения, которых надо избегать
• Динамический SQL. Для списка, содержащего цифры, этот метод может показаться проще, чем любой другой, но при его использовании возникают проблемы с правами. Еще более затруднительно применять его для списка строк. Для длинной входной строки производительность неприемлема, хотя для SQL 6.5 это единственный подходящий метод.
• Превращение списка в команду SELECT. Преобразует список с разделителями в одну или несколько команд INSERT. Довольно изящный способ, но не может обрабатывать входную строку длиннее ~35000 символов, к тому же больше никаких преимуществ нет.
• Очень медленные методы. Методы, которые используют charindex, patindex или LIKE. Эти решения очень медленны даже для короткой входной строки.
Если вам нужен короткий обзор различных методов, то на сайте Анит Сен (SQL Server MVP) http://www.bizdatasolutions.com/tsql/sqlarrays.asphttp://www.bizdatasolutions.com/tsql/sqlarrays.asp вы его найдете, хотя оценка автором методов не всегда сходится с моей.
Это первая из трех статей Херца Чена о преодолении невозможности вызова хранимых процедур в пользовательских функциях и командах SQL с помощью OPENQUERY. Здесь рассматривается одно из ограничений использования OPENQUERY и показано, как заставить OPENQUERY его обойти.
Одним из преимуществ СУБД является лежащий в ее основе язык запросов — SQL. Запрос всегда более краток и лучше оптимизирован, если вы можете написать его в виде одной команды SQL, а не в виде последовательности скриптов TSQL в пакете. Предположим, что у вас есть 200мегабайтная таблица, которая содержит полмиллиона записей. Если вы будете обновлять неиндексированный столбец по всей таблице одной командой UPDATE, это займет около двух минут. Но если вы сделаете то же самое, используя полмиллиона команд UPDATE в цикле WHILE, то это займет около трех часов. Однако существуют ситуации, в которых невозможно получить результат с помощью одной команды SQL. Единственной альтернативой является включение последовательных элементов в хранимую процедуру или пользовательскую функцию (userdefined function, UDF).
Конечно, пользовательские функции могут напрямую встраиваться в команды SQL. Они в известном смысле улучшают возможности выражений в SQL, потому что помогают SQL заключать в модули столько последовательных деталей, сколько нужно. Хранимые процедуры же не могут быть представлены в команде SQL, так что с точки зрения написания команды SQL функции являются более полезным инструментом.
К сожалению, функции не могут полностью заменить процедуры. На самом деле SQL Server устанавливает более жесткие ограничения на функции, чем на процедуры. В функциях вы не можете изменять глобальное состояние базы данных, например обновлять таблицу. Также SQL Server помещает большую часть своей системной информации в хранимые процедуры, например sp_who, а не в системные функции, такие как user_name(). Чтобы было легче сравнивать использование процедур и функций, заметим, что в одной только базе данных master в SQL 2000 существуют 973 хранимые процедуры, помогающие выполнять SELECT, UPDATE, INSERT или DELETE над абсолютно любой системной информацией. Однако существует всего лишь 101 функция, которая позволяет выполнять только SELECT, но не изменять конфигурацию, метаданные, безопасность, систему и системную статистическую информацию.
Иногда может понадобиться инкапсулировать какуюлибо системную информацию в одну команду SELECT. Например, было бы неплохо видеть информацию только о блокировках пользовательских процессов с помощью "SELECT spid, login, BlkBy FROM sp_who2 WHERE spid > 50". В другом случае вы можете захотеть выполнить сложные изменения состояния базы данных в одной команде INSERT, UPDATE или DELETE. К сожалению, интеллектуальные фантазии вроде этих никогда не сбудутся, по крайней мере, пока мы не сможем вызывать хранимые процедуры в команде SQL или внутри функции, которая в свою очередь встроена в SQL. На данный момент SQL Server запрещает вызов любой хранимой процедуры или обновление любой таблицы внутри функции — ограничение, которое не позволяет нам использовать системную информацию или выполнять сложные изменения в одной команде SQL. Эта статья — первая из цикла, цель которого — убрать барьер между функциями и процедурами, чтобы вы могли сочетать все лучшее из них в командах SQL.
Other
Переход с версии SQL Server 7.0 на версию SQL Server 2000 сохраняет прежнюю структуру папок, пока на сервере работает версия MSSQL8 (SQL 2000), в то же время при установке SQL Server 2000 создает совершенно иную структуру папок. Двоичные файлы в каталогах также могут отличаться, если сравнить изначальную установку и обновление. В этой статье администраторы баз данных Getty Images Том Роуш и Джим Уилсон показывают, как проводить обновление, чтобы машины выглядели, действовали и вели себя, как при изначальной установке.
Так, друзья, кто из вас переходил на версию SQL Server 2000 и сумел проделать это без помарок? Хочется увидеть лес рук... не стесняйтесь. Гм, спасибо, парень в последнем ряду, вы свободны. Остальная часть этой статьи адресована тем, кто испытал на себе чудеса установки больших программных приложений и знаком с сюрпризами, поджидающими на середине процесса установки или обновления, которые не упоминаются в документации.
Начальную установку SQL 2000 мы проводили на тестовом сервере. Первая проблема состояла в том, чтобы решить, где именно поместить программу на машине (иными словами, какой задать физический путь). Мы запускали программу установки и полагали, что, поскольку установленная у нас версия SQL 7 помещалась в папке MSSQL7, то логично будет предположить, что SQL 8 будет в папке MSSQL8.
Вы знаете, что получается, когда предполагаешь сделать то, что не следует. Что же натворили специалисты Microsoft? Как настоящие рецидивисты, они вернулись к старому доброму стандарту, которым пользовались во времена SQL 6.5. Тогда в структуре папок отсутствовал номер версии: папки назывались просто MSSQL. В версии SQL 7 папка стала называться MSSQL7.
В результате для всех наших разработческих и тестовых серверов мы ввели стандарт: помещать двоичные файлы в d:\MSSQL7, а данные и файлы журналов — в e:\MSSQL7. Тем из вас, кому еще только предстоит изведать радости обновления, скажу, что при «чистой» установке SQL 2000 создается система папок, структура которой не совпадает со структурой для версии SQL 7, в то время как при переходе от SQL 7 к SQL 2000 система папок остается прежней. Во всем этом проявляется некая таинственность, ведь, по существу, MSSQL8 работает в папках MSSQL7.
Когда же мы попытались восстановить базу данных на тестовом сервере (где файлы с данными размещались в e:\MSSQL8), то обнаружили, что они были вовсе не в e:\MSSQL8\data, а в e:\MSSQL8\MSSQL\data. (Заметим, между прочим, что теперь в структуре каталога мы находимся на один уровень глубже.) Проблема состояла в том, как перевести наши удаленные 40 серверов SQL с версии SQL 7 на SQL 2000 и (опятьтаки, между прочим) прийти, в конце концов, к совместимым путям и структурам каталогов с теми новыми машинами, на которых мы выполняли изначальную установку SQL 2000. Нас также тревожили возможные легкие различия (в двоичных файлах, записях в реестре и т. д.) между экземплярами SQL 2000, полученными обновлением SQL 7, и теми экземплярами SQL 2000, которые появились в результате «чистой» установки. Можете считать нас параноиками, но совместимость в нашей рабочей среде является непреложным требованием.
Первоначальный план
После долгих раздумий мы пришли к следующему плану:
1. Сделать резервные копии всех баз данных (и извлечь имена входа в систему).
2. Создать сценарии восстановления.
3. Обновить версию SQL 7 до SQL 2000.
4. Сформировать резервную копию MSDB.
5. Деинсталлировать SQL (все версии). Когда в этой точке вы деинсталлируете SQL 2000, вам сообщают, что производится удаление SQL 7.
6. Удалить все следы SQL (каталоги, ключи системного реестра и т. п.).
7. Установить SQL 2000.
8. Восстановить MSDB.
9. Установить SP2.
10. Восстановить пользовательские базы данных.
11. Запустить сценарии восстановления.
12. Все проверить.
13. Отметить!