О: Когда DDL, который создает новую таблицу, не задает значений NULL или NOT NULL явно, правила допустимости значения NULL в столбце таблицы определяют три параметра: ANSI NULL default, который устанавливается с помощью процедуры sp_dboption, и два параметра SET ANSI_NULL_DFLT_ ON и SET ANSI_NULL_DFLT_OFF, назначаемые командой SET.

Параметры SET ANSI_NULL_DFLT_ON и SET ANSI_NULL_DFLT_OFF являются взаимоисключающими. Только один из них может быть установлен в ON, т. е. установка одного из параметров в значение ON автоматически «присваивает» другому значение OFF. Эти параметры имеют преимущество перед установкой по умолчанию ANSI NULL, следовательно, установка значения по умолчанию ANSI NULL влияет на установку значений NULL для столбца, только если оба параметра имеют значение OFF. Согласно BOL, параметры по умолчанию ANSI NULL всегда установлены в ON для команды ALTER COLUMN, поэтому столбец допускает значение NULL до тех пор, пока разработчик не изменит установку по умолчанию. Когда тип данных для существующего столбца меняется, установки для всей базы не учитываются. Нужно явно указать ограничение NULL или NOT NULL, иначе столбец автоматически будет допускать значение NULL.

Также можно использовать функцию T-SQL getansimull(), которая возвращает значение ограничения NULL для данной базы для текущей сессии. Кроме того, нужно всегда явно указывать значения NULL или NOT NULL в операторах Data Definition Language (DDL). Не стоит полагаться на установки сервера по умолчанию, потому что они легко изменяются и часто отличаются от сервера к серверу. Чтобы получить дополнительную информацию о предложенных стандартах, загрузите «белые страницы» Джона Хиндмарша (John Hindmarsh) «SQL Server 7 Standarts» (http://www.sql7dba.com).

В: Есть ли в SQL Server системная хранимая процедура, которую я могу использовать для запуска сценария всякий раз, когда кто-нибудь изменяет таблицу в течение последних 24 часов?

О: Для решения данной задачи специальной процедуры не существует, но можно получать информацию напрямую из системных таблиц. Начните со столбца SCHRMA_VER системной таблицы master..sysobjects. Согласно описанию SQL Server Books Online (BOL), SQL Server увеличивает в нем целочисленное значение всякий раз, когда происходит изменение схемы таблицы. Документация на SCHRMA_VER не систематизирована, и у нас нет информации о правилах, которые определяют новый номер версии схемы после изменения. Однако нам достаточно знать хотя бы то, что номер версии будет другим, если схема таблицы изменится. Нужно написать код на языке T-SQL, который отвечает данной задаче. Могу предложить следующий простой способ решения.

Во-первых, создайте таблицу управления схемой:

Create table SchemaVerMgmt (TableName varchar,
CreateDate datetime SchemaVersion integer)

Во-вторых, раз в ночь (или с любым другим временным интервалом, подходящим для отслеживания изменений схемы) выполняйте SQL batch, который считывает значения столбцов NAME, CRDATE и SCHEMA_ VER FROM sysobjects WHERE type=?U?. Затем контрольная таблица сравнивается с результатами оператора SELECT и проверяется, отличаются ли значения в столбцах SCHEMA_VER. В нашей модели мы еще используем значение столбца CRDATE, который показывает, что кто-то создал таблицу. Эта дополнительная мера необходима для того, чтобы отследить изменение схемы таблицы посредством удаления таблицы и создания ее заново с таким же именем, но с другой схемой. В этом случае значение SCHEMA_VER может не измениться, но CRDATE сразу покажет, что таблица новая. Если еще добавить небольшой код, то можно вычислить учетную запись пользователя, который внес изменения.

В: Как мне построить собственные схемы блокировок на уровне приложения для управления доступом к критическим приложениям SQL Server?

О: Вообще обход ядра базы данных и изменение способа работы, в частности механизмов блокировок ядра, производить не рекомендуется. Механизм блокировок является важной частью кода ядра базы данных, и собственные схемы блокировок, как правило, не применяют. Однако иногда предоставление индивидуальной поддержки управления блокировками на уровне приложений имеет смысл. SQL Server 7.0 и предыдущие версии предоставляют разработчикам возможность построения индивидуальных систем управления блокировками с помощью таблицы, содержащей имя блокируемого ресурса приложения (например, процесс начисления заработной платы, который может выполнять один пользователь в определенной последовательности). Эти способы работают, но они могут и вызвать сбой. Любое приложение может легко сбиться, отслеживая, что заблокировано, а что нет, особенно при потере соединения.

SQL Server 2000 предоставляет дополнительные возможности индивидуального управления блокировками на уровне приложения. Для реализации этих возможностей используются новые процедуры: sp_getapplock и sp_releaseapplock. Приведем пример из описания SQL Server 2000 Books Online (BOL), показывающий применение новых процедур для блокировки и разблокировки ресурса Form1 на учебной базе данных Northwind:

- накладываем блокировку

USE Northwind
DECLARE @result int
EXEC @result = sp_getapplock @Resource = ?Form1?, 
@LockMode = ?Shared?

- снимаем блокировку

USE Northwind
EXEC sp_releaseapplock @LockResource = ?Form1?

Sp_getapplock предоставляет пять типов блокировок: Shared, Update, Exclusive, IntentExclusive, IntentShared. Для упрощения процесса освобождения ресурсов, блокировки связаны либо с текущей транзакцией, либо с текущей сессией. При подтверждении или откате транзакции блокировки, связанные с текущей транзакцией, освобождаются. При закрытии сессии также освобождаются связанные с ней блокировки.

В: И dBase и Paradox позволяют создавать индексы на основе выражений или функций, но мне не удается это сделать в SQL Server 7.0. Например, если я использую функцию LEFT для условия в запросе

SELECT field1, field2 FROM Table1
WHERE left(field1,5) = «xxxxx»

SQL Server 7.0 не будет использовать индекс, даже если существует какой-либо индекс по столбцу Field1. Будет ли эта функция работать, если я создам такой индекс

Index = Left(Field1,5)?

О: Нет, в SQL Server 7.0 и более ранних версиях нельзя создать индекс, который использует функции или выражения. Однако SQL Server 2000 позволяет индексировать непосредственно вычисляемые столбцы в таблице и размещать индексы в представлениях (views). Внимательно прочитайте раздел «The SET Options That Affect Results» в SQL Server 2000 Books Online (BOL), прежде чем настраивать расширенные типы индексов. Для индексов существует несколько ограничений. Например, любой индекс для вычисляемого столбца или представления работает, только если все операции, ссылающиеся на него, используют одинаковые алгоритмы для определения ключевых значений. Данное ограничение означает, что любые операторы SET, влияющие на результаты, возвращаемые операторами T-SQL, должны иметь одни и те же значения параметров для всех операций, которые ссылаются на индекс.

Семь параметров оказывают влияние на результаты в вычисляемых столбцах, которые возвращают представления. Для всех соединений, использующих индексы по вычисляемым столбцам или индексированные представления, необходимо установить шесть из этих параметров в значение ON: ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_ NULL_YIELDS_NULL и QUOTED_ IDENTIFIER. Параметр NUMERIC_ ROUNDABORT нужно установить в OFF.

В описании SQL Server 2000 BOL приводится следующий пример для иллюстрации сути этих ограничений:

CREATE TABLE Parts
(PartID int PRIMARY  KEY, PartName char(10),
PartMaterial char(10), PartColor char(10),
PartDescription AS  PartMaterial + PartColor)
GO
INSERT INTO Parts VALUES (1, ?Table?, ?Wood?, ?Red?)
INSERT INTO Parts VALUES (2, ?Chair?, ?Fabric?, ?Blue?)
INSERT INTO Parts VALUES (3, ?Bolt?, ?Steel?, NULL)
GO

Значение столбца PartDescription для строки, в которой PartID равен 3, зависит от значения параметра CONCAT_ NULL_YIELDS_NULL. Если он установлен в ON, то вычисляемым значением является NULL. Если же параметр CONCAT_NULL_YIELDS_ NULL установлен в OFF, то вычисляемым значением будет строка ?Steel?. Для обеспечения корректной работы индекса по столбцу PartDescription все операторы INSERT, UPDATE и DELETE должны применять установку параметра CONCAT_NULL_ YIELDS_ NULL, такую же, как в соединении, создавшем этот индекс. Оптимизатор не использует индекс для соединения, в котором установка параметра CONCAT_NULL_YIELDS_NULL отличается от установки в соединении, создавшем ключевые значения.

PartDescription на самом деле не существует. Это значение становится реальным только тогда, когда его сохраняет ключ индекса. Тем не менее значение ключа индекса изменяется, если изменяются некоторые из параметров настройки соединения (например, CONCAT_ NULL_ YIELDS_ NULL). SQL Server 2000 требует таких жестких мер для того, чтобы обеспечить корректную работу индексов на вычисляемых столбцах и представлениях.

КАРЕН УОТТЕРСОН

независимый журналист, редактор и консультант по клиент-серверным системам и хранилищам данных. Ей можно написать по адресу karen_watterson@msn.com.

БРАЙАН МОРАН

президент группы пользователей и директор по технологиям СУБД Spectrum Technology Group. Имеет сертификаты MCSE, MCSD и MCT. Ему можно написать по адресу brian@spectrumtech.com.