Как ускорить количество (*) в Interbase / Firebird

#count #firebird #interbase

#подсчет #firebird #interbase

Вопрос:

Interbase — это база данных поколений.

Это здорово, потому что откаты происходят почти мгновенно, но count(*) на это уходит вечность.
Это не похоже, например, на MySQL, где count может использовать индекс.

Я никогда не знал, почему, пока не увидел это:

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

В википедии:http://en.wikipedia.org/wiki/InterBase

Есть какие-нибудь советы о том, как выполнить быстрый подсчет в Interbase / Firebird

Ответ №1:

По этой ссылке:http://www.firebirdfaq.org/faq5

Есть другое решение. Эта статья написана Иваном Пренсилом, давним хакером Interbase и Firebird. Это решение возвращает только приблизительное количество записей. Как любезно объясняет Энн У. Харрисон: Любая запись, в которой был изменен первичный ключ, будет отображаться дважды, если старая версия не была собрана для мусора, а удаленные записи будут продолжать подсчитываться до тех пор, пока не будет собран мусор.

 /* first update the statistics */
UPDATE RDB$INDICES SET RDB$STATISTICS = -1;
COMMIT;

/* Display table names and record counts */
SELECT RDB$RELATIONS.RDB$RELATION_NAME,
CASE 
WHEN RDB$INDICES.RDB$STATISTICS = 0 THEN 0 
ELSE CAST(1 / RDB$INDICES.RDB$STATISTICS AS INTEGER) 
END 
FROM RDB$RELATIONS 
LEFT JOIN RDB$RELATION_CONSTRAINTS 
 ON RDB$RELATIONS.RDB$RELATION_NAME = RDB$RELATION_CONSTRAINTS.RDB$RELATION_NAME 
 AND RDB$CONSTRAINT_TYPE = 'PRIMARY KEY'
LEFT JOIN RDB$INDICES 
  ON RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME = RDB$INDICES.RDB$INDEX_NAME 
WHERE RDB$VIEW_BLR IS NULL AND RDB$RELATION_ID >= 128 
ORDER BY 1;
  

Это будет работать только с таблицами, имеющими первичный ключ.

Ответ №2:

Вы также могли бы создать 2 триггера для таблицы, которую хотите посчитать. Это решение предназначено для некоторых специальных таблиц, где у вас много «действий».

 CREATE TRIGGER TABLE_BI0 ACTIVE BEFORE INSERT
BEGIN
   UPDATE COUNTING_TABLE
   SET LINES=LINES   1
   WHERE TABLE='TABLE_NAME'; /* Table name here*/
END


CREATE TRIGGER TABLE_BD0 ACTIVE BEFORE DELETE
BEGIN
   UPDATE COUNTING_TABLE
   SET LINES=LINES - 1
   WHERE TABLE='TABLE_NAME'; /* Table name here*/
END
  

После этого, когда вам нужно узнать количество этой специальной таблицы, вы просто выбираете ее из COUNTING_TABLE

 SELECT LINES FROM COUNTING_TABLE
WHERE TABLE='TABLE_NAME' /* Table name here*/
  

Комментарии:

1. Это, по-моему, плохая идея, особенно для таблиц с большим количеством «действий». Все параллельные операции над таблицей, которые в остальном независимы, будут пытаться записать одну и ту же запись в COUNTING_TABLE , так что это может привести к конфликтам блокировок.

2. Вы правы @mghie , но если у него много действий, это, безусловно, происходит за очень короткие транзакции, что сводит к минимуму время блокировки / конфликты. Перед применением этого решения следует очень серьезно принять во внимание ваши соображения.