Работа с базой данных из портала

Соединимся с базой TestDB подобно тому, как мы соединялись с БД master на Рис.6-7 предыдушего поста. Типовые задачи работы с ней сгруппированы внизу левой панели. Модный плиточный интерфейс - это первый пункт (Overview - см. Рис.1 предыдущего поста):

Рис. 1

Статистика использования - Administration (см. Рис.3 предыдущего поста):

Рис. 2

Мы сейчас идем в третий пункт - Design, позволяющий создавать основные объекты базы.

Рис. 3

Кликаем по New Table и задаем структуру новой таблицы. Можно видеть, что форма ввода очень похожа на интерфейс табличного дизайнера в SSMS:

Рис. 4

После сохранения таблицы поменять признак Is Identity у колонки нельзя. Можно досоздать к ней индексы или тут же набить в нее данные, кликнув на соответствующий пункт справа от Columns:

Рис. 5

В ячейках работает Ctrl-C/Ctrl-V. Ширины колонок можно менять, однако, если набить часть записей и сохранить их в таблицу, ширины колонок снова съезжают. Понятно, что этот интерфейс не будет являться основным способом ввода данных в таблицы SQL Azure так же, как никто не пользуется для этих целей, например, SSMS. Так, посмотреть, при случае, подкорректировать.

После того, как таблица создана, в режим ее редактирования можно вернуться, нажав Edit. Edit и Dependencies изначально не показываются. Чтобы они проявились, нужно навести курсор на строку с таблицей.

Рис. 6

Разумеется, таблицы можно создавать при помощи оператора CREATE TABLE. Его можно выполнить, например, из запросного окна на портале (см. Рис.4 предыдущего поста), которое откроется по нажатию кнопки New Query в верхней строке меню на Рис.6. Особенностью SQL Azure является то, что каждая таблица обязана иметь custered key, т.е. быть организована в виде дерева, а не валяться бесформенной кучей. Это основополагающее ограничение, жалко, что Делани про него не пишет. Мы коснемся его в федерациях. Я так думаю.

if object_id('Продукт', 'U') is not null drop table [Продукт]
go
create table [Продукт] (
 ProductID int identity(1, 1) primary key clustered,
 ProductName nvarchar(40) not null,
 CategoryID int foreign key references [Категория](CategoryID),
 UnitPrice smallmoney,
 InStockSince date default sysdatetime()
)


Рис. 7

Если теперь кликнуть на Design в левой панели, мы вернемся в список таблиц Рис.6, обновив который (Refresh в верхнем меню), увидим только что созданную таблицу [Продукт]:

Рис. 8

Еще создадим для демонстрации хранимую процедуру, при помощи которой будем добавлять записи в таблицу Категория. Кликaем на Stored Procedures. Процедура uspAddCategory будет принимать три параметра, соответствуюшие полям CategoryName, Description и Picture и состоять из тривиального оператора insert [Категория] (CategoryName, Description, Picture) values (@Category, @Description, @Picture):

Рис.9

Обратите внимание, что значение по умолчанию для параметра типа varbinary здесь задать не удается, хотя, в принципе, это ничему не противоречит. Портал SQL Azure не любит ни вводить, ни отображать значения типов binary/varbinary, в частности, он не отображает их в панели результатов.

Я планирую набить таблицу Категория блобами, чтобы размер базы TestDB достиг установленного ей максимального размера - см. Рис.2 предыдущего поста. В документации сказано, что добавление в нее новых данных становится невозможно, но старые данные остаются доступны на чтение. Мне интересно, можно ли их при этом обновлять. По логике вещей, если это апдейт in-place, который просто перетирает старое значение и не требует увеличения существующего объема за счет, например, сплита страниц, то можно. В целом, апдейт in-place может иметь место, если обновляется не-nullовое поле фиксированной длины, кластерный ключ при этом не затрагивается и ключевые поля некластерного индекса либо не затрагиваются, либо он уникален. Апдейт in-place, по определению, невозможен, если на таблицу повешен триггер или она участвует в репликации или CDC.

Можно тупо заполнить varbinary(max) константным байтом до достижения необходимой длины, но это неинтересно. Пусть лучше байты будут случайными. Случайную последовательность байт в T-SQL умеет производить CryptoAPIшная функция CRYPT_GEN_RANDOM(<длина>). Мы касались ее в посте Выбрать случайную запись из таблицы. Замечательная функция, однако входящий параметр <длина> у нее ограничен 8000 байтами. Маловато будет. Для 2008 R2 у меня была писана на ее основе UDF, которая преодолевает это ограничение:

if OBJECT_ID('ufnGenereateRandomVarbinaryMax', 'FN') is not null drop function ufnGenereateRandomVarbinaryMax
go
create function ufnGenereateRandomVarbinaryMax(@n smallint, @kbmb nvarchar(2)) returns varbinary(max) as begin
if @n <= 0 return null
declare @nn bigint = case upper(@kbmb) when 'КБ' then @n * 1024 when 'МБ' then @n * 1024 * 1024 else @n end
declare @MaxSize int = 8000
declare @i int = @nn / @MaxSize; declare @r int = @nn - @i * @MaxSize
declare @b varbinary(max) = cast(replicate(cast(CRYPT_GEN_RANDOM(@MaxSize) as varbinary(max)), @i) as varbinary(max))
if @r <> 0 set @b += CRYPT_GEN_RANDOM(@r)
return @b
end
go
select len(dbo.ufnGenereateRandomVarbinaryMax(10, 'МБ')), @@version


Рис.10

Шаблона для создания пользовательской функции в отличие от процедуры в Хьюстоне не предусмотрено, поэтому открываем на портале запросное окно, как на Рис.7, и копируем туда код создания функции из Рис.10:

Рис.11

Как в сад? Нет, я понимаю, что функция CRYPT_GEN_RANDOM() недетерминирована и все такое, но нормальному SQL Server 2008 R2 SP1 это пополам, а SQL Azure кочевряжится. Это зря она так.

Для справки: что из функциональности SQL Server 2008 не умеет SQL Azure, приведено в BOL.

Идем в пункт Design (слева внизу) -> Views (Сверху) - см. Рис.3 - и создаем новое представление по имени CRYPT_GEN_RANDOM с телом select CRYPT_GEN_RANDOM(8000) as RandomBinary8K:

Рис.12

Заменяем в пользовательской функции Рис.11 внутренние вызовы функции CRYPT_GEN_RANDOM на обращения к одноименному представлению Рис.12:

create function ufnGenereateRandomVarbinaryMax(@n smallint, @kbmb nvarchar(2)) returns varbinary(max) as begin
if @n <= 0 return null
declare @nn bigint = case upper(@kbmb) when 'КБ' then @n * 1024 when 'МБ' then @n * 1024 * 1024 else @n end
declare @MaxSize int = 8000
declare @i int = @nn / @MaxSize; declare @r int = @nn - @i * @MaxSize
declare @b varbinary(max) = cast(replicate(cast((select RandomBinary8K from CRYPT_GEN_RANDOM) as varbinary(max)), @i) as varbinary(max))
set @b += cast(left((select RandomBinary8K from CRYPT_GEN_RANDOM), @r) as varbinary(max))
return @b
end


Рис.13

Теперь, когда функция работает, открываем еще один New Query и пишем скрипт наполнения таблички Категория:

declare @Picture varbinary(max), @i tinyint = 0
while @i < 20 begin
select @Picture = dbo.ufnGenereateRandomVarbinaryMax(100, 'МБ'), @i += 1
exec uspAddCategory @Picture = @Picture
select @i as Итерация, (sum(reserved_page_count) * 8192.0) / 1024 / 1024 as [Размер базы в метрах] from sys.dm_db_partition_stats
end
 
select CategoryID, CategoryName, Description, Len(Picture) from [Категория]


Рис.14

Выяснилась любопытная особенность: SQL Azure не моментально обнаруживает, что база превысила отведенный ей лимит:

Рис.15

Рис.16

Работа с "перегретой" базой является нестабильной, соединение несколько раз рвется, тем не менее принципиальная возможность работы сохраняется. Интересно, как с меня в этом случае будут брать деньги: как за 3 гига или за 1? Вообще-то я создавал базу с максимальным размером в 1 ГБ и, по идее, больше этого платить не обязан.

Проходит 3-5 минут, прежде чем появляется сообщение об ошибке переполнения при попытке выполнить INSERT или UPDATE:

Msg 40544, Level 20, State 5, Line 1

The database has reached its size quota. Partition or delete data, drop indexes, or consult the documentation for possible resolutions.

Рис.17

Операторы SELECT и DELETE продолжают работать. Необходимо либо увеличить MaxSize, как показывалось на Рис.10 предыдущего поста, либо удалить лишнее, приведя БД в пределы MaxSize:

delete from [Категория] where Picture is not null

После удаления ошибка Рис.17 продолжает выдаваться еще несколько минут на попытки обновления, удаления, создания новых объектов и т.п., пока SQL Azure не сообразит, что объем пользовательских данных в базе уменьшился.

Читатели уже обратили внимание, что слева в панели под названием базы данных перечислены открытые активности: окна запросов, создания таблицы, процедуры, представления, окно статистики по базе данных. Кликнув по заголовку My Work, можно собрать их уменьшенные изображения на "рабочем столе", что позволяет, например, быстро закрыть несколько ненужных, не раскрывая их в полный размер.

Рис.18