хранимые процедуры
Механизм базы данных Access в режиме запросов ANSI-92 поддерживает синтаксис CREATE PROCEDURE
(SQL DDL), например.
CREATE PROCEDURE GetCompanies
(
:company_type VARCHAR(30) = NULL
)
AS
SELECT company_registered_number, organisation_name, company_type
FROM Companies
WHERE company_type = IIF(:company_type IS NULL, company_type, :company_type);
Таким образом, результирующий объект является PROCEDURE
и сохраняется в файле базы данных вместе с таблицами. Акцент здесь делается на слове «сохранено» (а не «процедура»), то есть оно «близко к данным». Использование этих объектов способствует хорошему отделению передней части (FE) от задней части (BE), и я имею в виду логический, а не физический; например, SQL-код, хранящийся в коде VBA или в свойствах элемента управления Access Forms, не «близок к данным» и смешивает внутренний «слой» с внешним «слоем» и затрудняет обслуживание SQL-кода. например если вам нужно переименовать столбец в таблице, задача проста, если все, что вам нужно сделать, это просмотреть PROCEDURE
s и VIEW
s.
Еще одним преимуществом использования PROCEDURE
является (или, скорее, было) то, что в сочетании с безопасностью на уровне пользователя (ULS) это может помочь в «удобстве использования». Чтобы использовать пример, часто спрашивают, как добавить столбец созданный_дат в таблицу и сохранить его значение. Добавление DEFAULT
текущей временной метки дает вам только часть пути, например
CREATE TABLE Entities (
entity_ID CHAR(8) WITH COMPRESSION NOT NULL UNIQUE,
CONSTRAINT entity_ID__pattern
CHECK (entity_ID NOT ALIKE '%[!0-9]%'),
entity_name VARCHAR(20) NOT NULL,
CONSTRAINT entity_name__whitespace
CHECK (
entity_name NOT ALIKE ' %'
AND entity_name NOT ALIKE '% '
AND entity_name NOT ALIKE '% %'
AND LEN(entity_name) > 0
),
created_date DATETIME DEFAULT NOW() NOT NULL
);
Но это не мешает явному значению, которое не является текущей отметкой времени. Конечно, мы могли бы добавить ограничение CHECK
или Правило проверки, чтобы обеспечить выполнение этого:
ALTER TABLE Entities ADD
CONSTRAINT entity_created_date__must_be_current_timestamp
CHECK (created_date = NOW());
Проблема здесь в том, что ограничение CHECK
и Правила проверки проверяются на уровне строк, т. Е. Если вы когда-либо пытаетесь изменить другой столбец, ограничение будет кусаться. Не хорошо, так что:
ALTER TABLE Entities DROP
CONSTRAINT entity_created_date__must_be_current_timestamp;
Что делать? Один из подходов состоит в том, чтобы удалить привилегии из таблицы, чтобы конечные пользователи (и приложения в этом контексте тоже пользователи) не могли INSERT
или UPDATE
данные таблицы напрямую, а затем создать PROCEDURE
s, чтобы данные могли быть изменен и вместо этого предоставит соответствующие привилегии PROCEDURE
s, например
CREATE PROCEDURE AddEntity (
:entity_ID CHAR(8),
:entity_name VARCHAR(20)
)
AS
INSERT INTO Entities (entity_ID, entity_name, created_date)
VALUES (:entity_ID, :entity_name, NOW());
EXECUTE EXECUTE AddEntity '00000001', 'Black';
Я использую прошедшее время, потому что, как вы, возможно, знаете, команда Access (или это была команда SharePoint? :)) удалила ULS из механизма ACE new-for-Access2007. Я не уверен, что могу порекомендовать использовать устаревшую функцию.
Теперь плохие новости. Многие (большинство?) Люди будут утверждать, что такой PROCEDURE
не является процедурой, и у них есть хорошая точка зрения, потому что синтаксис SQL ядра СУБД Access не поддерживает управление потоком, объявление переменных, даже возможность выполнения более одного оператора SQL. Другими словами, PROCEDURE
не может содержать процедурный код. Рассмотрим таблицу, которая ссылается на сущности:
CREATE TABLE FlyingEntities (
entity_ID CHAR(8) WITH COMPRESSION NOT NULL UNIQUE
REFERENCES Entities (entity_ID)
ON DELETE CASCADE
ON UPDATE CASCADE
);
Было бы неплохо иметь PROCEDURE
, который может создать строку в сущностях и, необязательно, создать строку в FlyingEntities на основе значения параметра, но это просто невозможно в одном операторе SQL. Поэтому ядро базы данных Access PROCEDURE
имеет ограниченную ценность, особенно теперь, когда ULS исчез.
Триггеры
Нельзя обойти стороной тот факт, что ядро базы данных Access не имеет и никогда не имело триггеров. Вопрос в том, нужны ли они вам?
Несмотря на то, что я неравнодушен к простоте ядра СУБД Access, правда состоит в том, что много лет назад я перевел всю «серьезную» работу в более «промышленную мощь» и более совместимые с SQL Standard продукты, в первую очередь SQL Server. Однако в SQL Server я использую триггеры только для двух вещей, обе из которых могут быть выполнены без триггеров (в определенной степени) в ядре базы данных Access.
Первый из этих способов - справиться с тем, что ограничения CHECK
SQL Server не поддерживают подзапросы; другими словами, они могут быть на уровне столбца и на уровне строки, но не на уровне таблицы. Ограничения ядра доступа к базе данных CHECK
, введенные в Jet 4.0 и все еще присутствующие в ACE (2007), всегда на уровне таблиц ... ну, они в теории. Существует проблема (предполагаемая ошибка), когда они проверяются на уровне строк, когда их следует логически проверять на уровне операторов SQL. Они не поддерживают синтаксис SQL-92 DEFERRABLE
, поэтому для этой проблемы не существует обходного пути (кстати, SQL Server испытывает ту же проблему при использовании FUNCTION
для обхода ограничения без подзапросов). Не все ограничения CHECK
столкнутся с этой проблемой, но ее существование заставляет меня немного насторожиться.
Второе и последнее использование триггеров в SQL Server для меня связано с другим ограничением: страшный «FOREIGN KEY ... может вызывать циклы или несколько каскадных путей» при попытке создать два REFERENCE
s для одного и того же ключа, например. это разрешено в движке базы данных Access:
CREATE TABLE Marriages (
entity_ID_1 CHAR(8) WITH COMPRESSION NOT NULL UNIQUE
REFERENCES Entities (entity_ID)
ON DELETE CASCADE
ON UPDATE CASCADE,
entity_ID_2 CHAR(8) WITH COMPRESSION NOT NULL UNIQUE
REFERENCES Entities (entity_ID)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT cannot_marry_yourself
CHECK (entity_ID_1 <> entity_ID_2)
);
Но перенести это на SQL Server (удалить WITH COMPRESSION
и т. Д.), И это не разрешено. В этом случае never_marry_yourself будет предотвращать циклы, но SQL Server выполняет простой подсчет и решает, что 1 + 1 = слишком много. Грубый, но эффективный, я полагаю. Использование триггеров - единственный способ удовлетворительного обходного пути; CASCADE
ссылочные действия - особая боль с триггерами.
С другой стороны, ядро базы данных Access в каком-то смысле даже тупее, чем SQL Server, потому что оно вообще не пытается обнаруживать циклы. Если вы создадите цикл, вы не получите предупреждения, и результатом будет гонка за перезапись данных последней и трудная ситуация для отладки.
Кроме этих случаев, я избегаю триггеров, потому что они являются головной болью при техническом обслуживании (если вы можете получить их правильно с самого начала). Я потерял счет раз, когда коллеги обращались ко мне за помощью, когда мы оба были сбиты с толку тем, в чем может быть проблема только для них, чтобы они застенчиво сказали мне позже, что был триггер, который они забыли, который они создали.
Так что, да, ядру базы данных Access не хватает триггеров, но вы можете обнаружить, что вам может быть лучше без них.
О, и не начинайте меня с документацией для ядра базы данных Access. Он фрагментирован, и многие из этих фрагментов со временем исчезли, и многие из них не существовали, например. Я упомянул CHECK
ограничения выше, но никогда не было никаких подробностей, просто пара некорректных примеров (все, что я знаю об CHECK
ограничениях, которые мне пришлось выучить методом проб и ошибок - то, что существует, на что я не наткнулся еще ?!) И фрагменты, которые существуют, содержат материальные ошибки и ошибки упущения ... даже ошибочно детализирующие функциональность, которой никогда не было! например CREATE TABLE Инструкция из справки Access2007 упоминает временные таблицы с именами NOT NULL
ограничений и многостолбцовых NOT NULL
ограничений, которые не существуют, но не упоминают DEFAULT
или тот факт, что некоторые CONSTRAINT
s не реализованы с использованием индексов. Но самое серьезное упущение IMO - это ссылка на выражения ядра базы данных Access, например, IIF()
ведет себя не так, как IIf()
в VBA, но, похоже, это в настоящее время недокументировано. Справка SQL для Jet 3 имела такой список, никакой версии с тех пор не было, и справка Jet 3 исчезла из MSDN год или два назад. Отсутствие хорошей документации действительно подрывает доверие к движку базы данных Access.