Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Диплом MET 1_1.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
6.47 Mб
Скачать

Into :TheInitialValue;

If( TheInitialValue is null ) then exit;

SELECT

SUM(AMovement.Quantity * AMovement.UnitCost)

FROM

AMovement

INNER JOIN AnEconomicOperation EOp ON (AMovement.EconomicOperationId = EOp.Id)

WHERE

(AMovement.DebitAccountId IN (SELECT SubAccountId FROM AccountInheritance WHERE (AccountFolderId = :AccountId)))

AND

(EOp.BeginsWith < :ReqDate)

Into :TheSum;

IF ( TheSum IS NULL ) THEN TheSum = 0;

DebitBalance = TheInitialValue + TheSum;

END

Бывает, что соединения таблиц выполняются SQL-серверами более эффективно, чем вложенные запросы. Соответственно, вложенный запрос в этой хранимой процедуре можно переписать как соединение таблиц, однако, во-первых, оптимизаторы многих SQL-серверов в таком случае автоматически производят реинтерпретацию, во-вторых, подобные ручные оптимизации стоит проводить, только если производительность сервера оказывается недостаточной на рабочих объёмах данных.

Для поддержки актуальности таблицы "Наследование счёта" необходимо описать соответсвующие триггеры:

SET TERM !! ;

CREATE PROCEDURE GetAccountParents(ASubAccountId INTEGER) RETURNS(AnAccountFolderId INTEGER)

AS

BEGIN

/* Selects thread of parents of account from bottom to top */

WHILE( EXISTS(SELECT * FROM ASubAccount WHERE (Id = :ASubAccountId)) ) DO

BEGIN

SELECT AccountFolderId FROM ASubAccount WHERE (Id = :ASubAccountId) INTO :AnAccountFolderId;

SUSPEND;

ASubAccountId = AnAccountFolderId;

END

END !!

SET TERM ; !!

SET TERM !! ;

CREATE TRIGGER TI_AnAccount_Inherit FOR AnAccount

AFTER INSERT

AS

DECLARE VARIABLE NewId INTEGER;

DECLARE VARIABLE ParentId INTEGER;

BEGIN

NewId = NEW.Id;

Insert into AccountInheritance(AccountFolderId, SubAccountId) values(:NewId, :NewId);

FOR

SELECT AnAccountFolderId FROM GetAccountParents(:NewId) INTO :ParentId

DO

BEGIN

Insert into AccountInheritance(AccountFolderId, SubAccountId) values(:ParentId, :NewId);

END

END !!

SET TERM ; !!

SET TERM !! ;

CREATE TRIGGER TD_AnAccount_Inherit FOR AnAccount

AFTER DELETE

AS

DECLARE VARIABLE OldId INTEGER;

BEGIN

OldId = OLD.Id;

DELETE FROM AccountInheritance WHERE (SubAccountId = :OldId);

END !!

SET TERM ; !!

COMMIT;

Кроме того, определим несколько представлений для удобства восприятия БД:

CREATE VIEW AnAccountTreeView (Id, AccountFolderId, Code, DisplayLabel) AS

SELECT ACustomAccount.Id, ASubAccount.AccountFolderId, ACustomAccount.Code, ACustomAccount.DisplayLabel

FROM ACustomAccount LEFT OUTER JOIN ASubAccount ON ( ACustomAccount.Id = ASubAccount.Id)

Многие разработчики из соображений оптимизации информационной системы по скорости также хранят такие промежуточные данные, как обороты по счетам и корреспондирующим счетам за определённый промежуток времени. По моему мнению, подтверждённому личной практикой, такая оптимизация не оправдывает себя как по выигрышу в скорости, так и по затратам на реализацию.

Специализация шаблонов физических объектов

CASE-инструментарий предоставляет возможность описывать шаблоны таких объектов БД, как хранимые процедуры и триггеры. Кроме того, можно описывать шаблоны скриптов.

Для независимых сущностей мы определяем генераторы для суррогатных ключей и хранимые процедуры для создания записей.

Шаблон CreateGenerator (PreScript For Table)

CREATE GENERATOR %TableNameGenerator%DBMSDelim

SET GENERATOR %TableNameGenerator TO 0%DBMSDelim

Шаблон CreateRec (Stored Procedure For Table)

CREATE PROCEDURE Create%TableNameRec(%ForEachAtt() {

%If( %Not( %AttIsPK) ) {

%AttFieldName %AttPhysDatatype, } }

Dummy INTEGER) RETURNS(Id INTEGER)

AS

BEGIN

Id = GEN_ID(%TableNameGenerator, 1)%DBMSDelim

INSERT INTO %TableName(%ForEachAtt() {

%If( %Not( %AttIsPK) ) {

%AttFieldName, } } Id) VALUES ( %ForEachAtt() {

%If( %Not( %AttIsPK) ) {

:%AttFieldName, } } :Id )%DBMSDelim

END

Создание деловых правил

В числе прочих мы можем создать, например, такие правила:

SET TERM !! ;

CREATE PROCEDURE CreateAnAccountRec(Code VARCHAR(20), DisplayLabel VARCHAR(80),

IsActive INTEGER, IsPassive INTEGER,

InBalance INTEGER, ParentCode VARCHAR(20), Dummy INTEGER) RETURNS(Id INTEGER)

AS

DECLARE VARIABLE ParentAccountId INTEGER;

DECLARE VARIABLE ParentInBalance INTEGER;

DECLARE VARIABLE ParentIsActive INTEGER;

DECLARE VARIABLE ParentIsPassive INTEGER;

BEGIN

IF( (IsActive = 0) AND (IsPassive = 0) ) THEN

BEGIN

IsActive = 1;

IsPassive = 1;

END

SELECT Id, InBalance, IsActive, IsPassive

FROM ACustomAccount

WHERE Code = :ParentCode

INTO :ParentAccountId, :ParentInBalance, :ParentIsActive, :ParentIsPassive;

IF( ParentAccountId IS NULL ) THEN

BEGIN

ParentInBalance = InBalance;

ParentIsActive = IsActive;

ParentIsPassive = IsPassive;

END

EXECUTE PROCEDURE CreateACustomAccountRec(Code, DisplayLabel, ParentIsActive, ParentIsPassive,

ParentInBalance, Dummy) RETURNING_VALUES(Id);

/* check for parent account */

IF( (Id > 0) AND (ParentAccountId IS NOT NULL) ) THEN

BEGIN

/* check parent account for folder */

IF( NOT EXISTS( SELECT * FROM AnAccountFolder WHERE Id = :ParentAccountId ) ) THEN

BEGIN

INSERT

INTO AnAccountFolder(Id)

VALUES(:ParentAccountId);

END

INSERT

INTO ASubAccount(Id, AccountFolderId)

VALUES(:Id, :ParentAccountId);

END

END !!

SET TERM ; !!

SET TERM !! ;

CREATE PROCEDURE DoMovement(DebitAccountId INTEGER, CreditAccountId INTEGER, EcOpId INTEGER,

Quantity INTEGER, UnitCost DOUBLE PRECISION) RETURNS(Id INTEGER)

AS

BEGIN

IF( NOT EXISTS( SELECT * FROM AnAccount WHERE Id = :DebitAccountId ) ) THEN

BEGIN

INSERT INTO AnAccount(Id, InitialDebitValue, InitialCreditValue) VALUES(:DebitAccountId, 0.0, 0.0);

END

IF( NOT EXISTS( SELECT * FROM AnAccount WHERE Id = :CreditAccountId ) ) THEN

BEGIN

INSERT INTO AnAccount(Id, InitialDebitValue, InitialCreditValue) VALUES(:CreditAccountId, 0.0, 0.0);

END

EXECUTE PROCEDURE CreateAMovementRec(EcOpId, CreditAccountId, DebitAccountId, 'NOW',

Quantity, UnitCost, 0) RETURNING_VALUES(ID);

END !!

SET TERM ; !!

SET TERM !! ;

CREATE PROCEDURE DoMovementByCode(DebitAccountCode VARCHAR(20), CreditAccountCode VARCHAR(20),

EcOpId INTEGER, Quantity INTEGER,

UnitCost DOUBLE PRECISION) RETURNS(Id INTEGER)

AS

DECLARE VARIABLE DebitAccountId INTEGER;

DECLARE VARIABLE CreditAccountId INTEGER;

BEGIN

SELECT Id FROM ACustomAccount WHERE Code = :DebitAccountCode INTO :DebitAccountId;

SELECT Id FROM ACustomAccount WHERE Code = :CreditAccountCode INTO :CreditAccountId;

EXECUTE PROCEDURE DoMovement(DebitAccountId, CreditAccountId, EcOpId, Quantity, UnitCost)

RETURNING_VALUES(ID);

END !!

SET TERM ; !!

SET TERM !! ;

CREATE PROCEDURE GetDebitBalanceAllOn(ReqDate DATE) RETURNS(DebitBalance DOUBLE PRECISION)

AS

DECLARE VARIABLE TheInitialValue DOUBLE PRECISION;

DECLARE VARIABLE TheSum DOUBLE PRECISION;

BEGIN

SELECT

SUM(A.InitialDebitValue)

FROM

AnAccount A

INNER JOIN ACustomAccount CA ON (A.Id = CA.Id)

WHERE

(CA.InBalance = 1)