Шрифт:
. . .
DECLARE VARIABLE DBK CHAR (8);
/* 8 символов для db_key таблицы А */
. . .
FOR SELECT B.FK,
SUM(B.VALUEFIELD) ,
A. RDB$DB_KEY
FROM TABLEB В
JOIN TABLEA A ON A.PK = B.FK
WHERE <условия>
GROUP BY B.FK, A.RDB$DB_KEY
INTO :B_FK, :TOTAL, :DBK DO
UPDATE TABLEA SET A.TOTAL = :TOTAL
WHERE A.RDB$DB_KEY = :DBK;
! ! !
ПРИМЕЧАНИЕ. В Firebird нет необходимости в ключевом столбце для создания соединения, однако он нужен в списке SELECT, чтобы предложение GROUP BY было допустимым.
. ! .
Перечислим преимущества такого подхода.
* Фильтрация общих записей для А и В будет эффективной, когда оптимизатор может создать хороший фильтр для явного соединения.
* Если соединение может применить свое собственное предложение поиска, то существует выгода от выполнения фильтрации до того, как изменение будет проверять свое собственное условие.
* Строки таблицы правой стороны (А), локализованные с помощью прямых указателей db key, выделяются во время соединения быстрее, чем при просмотре первичного ключа или его индекса.
Поскольку добавление не включает поиск, наиболее простые операции добавления - например, чтение константных значений из импортируемого набора во внешней таблице - не требуют локализации ключей.
Однако не все входные значения(VALUE2) оператора INSERT получаются так просто.
Это может быть очень сложным набором значений, полученным из выражений, со-
единений или агрегатных операций. В хранимой процедуре операция INSERT может разветвляться в предложении ELSE предиката IF (EXISTS (...)), например:
IF EXISTS(SELECT...) THEN
. . .
ELSE
BEGIN
INSERT INTO TABLEA
SELECT
C.PKEY,
SUM (B. AVALUE) ,
AVG(B.BVALUE),
COUNT(DISTINCT C.XYZ)
FROM TABLEB B JOIN TABLEC С
ON B.X = C.Y
WHERE C.Z = 'value'
AND С.PKEY NOT IN(SELECT PKEY FROM TABLEA)
GROUP BY С.PKEY;
END
. . .
Реализация этого в хранимой процедуре:
FOR SELECT
С.PKEY,
SUM(B.AVALUE),
AVG(B.BVALUE),
COUNT(DISTINCT C.XYZ)
FROM TABLEB B JOIN TABLEC С
ON B.X = C.Y
WHERE C.Z = 'value'
AND С.PKEY NOT IN(SELECT PKEY FROM TABLEA)
GROUP BY С.PKEY
INTO :C_KEY, :TOTAL, :B_AVG, :C_COUNT DO
BEGIN
SELECT A.RDB$DBKEY FROM TABLEA A
WHERE A.PKEY = :C_KEY
INTO :DBK;
IF (DBK IS NULL) THEN /* строка не существует */
INSERT INTO TABLEA(PKEY, TOTAL, AVERAGE_B, COUNT_C)
VALUES(:C_KEY, :TOTAL, :B_AVG, :C_COUNT);
ELSE
UPDATE TABLEA SET
TOTAL = TOTAL + :TOTAL,
AVERAGE_B = AVERAGE_B + :B_AVG,
COUNT_C = COUNT_C + :C_COUNT
WHERE A. RDB$DB_KEY = : DBK;
END
По умолчанию областью действия db key является текущая транзакция. Вы можете считать, что он остается правильным во время действия текущей транзакции. Подтверждение или откат транзакции приведет к тому, что значения RDB$DB_KEY станут непредсказуемыми. Если вы используете commitRetaining, контекст транзакции сохраняется, блокируя сборку мусора и, следовательно, предотвращая "переназначение" старого db_key. При этих условиях значения RDB$DB_KEY для любых используемых строк в вашей транзакции сохраняются действительными, пока не произойдет "жесткое" подтверждение или откат.
После жесткого подтверждения или отката другая транзакция может удалить строку, которая была изолирована внутри контекста вашей транзакции и, следовательно, рассматривалась как "существующая" в вашем приложении. Любое значение RDB$DB_KEY теперь может указывать на несуществующую строку. Если у вас достаточно большой интервал между моментом, когда начинается ваша транзакция и когда завершается ваша работа, вы должны проверять, не была ли за это время строка изменена или заблокирована другой транзакцией.
Некоторые интерфейсы приложений, например IB Objects, являются суперинтеллектуальными в плане добавлений и могут подготовить "сегмент" для вновь добавленных строк в клиентских буферах для быстрого обновления списка после подтверждения. Такие возможности важны для производительности при работе в сети. Однако "интеллектуальность", подобная этой, основывается на точных реальных ключах. Поскольку db_key является просто заменителем ключа для набора, наследуемого от предыдущих подтвержденных данных, он не имеет смысла для новой строки - он не доступен при изменениях в клиентском буфере.