Оптимизация приложений для работы с СУБД InterBase - Ведение подсчета записей
ОГЛАВЛЕНИЕ
Ведение подсчета записей
Важно понимать, что каждая из идей подсчета записей неуместна в случае многопользовательской архитектуры программы, за исключением случая с использованием транзакций в режиме snapshot. Без такого уровня изоляции невозможно точно подсчитать количество записей и выбрать их в контексте одной операции. Так, если на момент выборки существуют другие пользователи, работающие с таблицей (таблицами), "действительное" количество записей может измениться с момента подсчета записей до момента, когда они выбраны. В мире Delphi и C++ Builder все еще сложнее: свойство TDataSet.RecordCount слабо отражает действительное положение вещей, то есть не отображает реального количества информации в наборе данных. Более того, каждый из наборов компонент, будь то IBX, IBO, dbExpress или BDE, имеет свои реализации подсчета записей. И ни одна из реализаций на другую не похожа.
И, наконец, не забывайте, что многоверсионная структура данных в СУБД InterBase, хоть и имеет массу преимуществ, делает процесс подсчета записей в больших таблицах крайне затратной операцией — это особенно заметно влияет на производительность, если очистка базы данных (sweep process) производилась давно. Серверу просто приходится проверять каждую запись на предмет попадания в область видимости текущей транзакции. По моему мнению, существует, если вообще есть, буквально пара-тройка случаев, когда использование свойства RecordCount или оператора SQL COUNT может быть обоснованным. Такие ошибки, как правило, следует искать в программах, показывающих очень слабую производительность, которые вы сами не писали, но вас попросили улучшить их скорость работы.
Для подобных случаев существует также несколько альтернативных вариантов реализации. Один из них — перед вами. Так, чтобы определить, не пуста ли таблица, лучше использовать свойство IsEmpty:
if not MyTable.IsEmpty then begin
DoSomething;
end
else begin
raise Exception.Create (NORECORDS);
end;
Далее — если необходимо определить в хранимой процедуре или триггере, пуста ли таблица. Пример ниже возвращает "таблица не пуста", если в ней (таблице) существует хотя бы одна запись:
SELECT "таблица не пуста"
FROM RDB$DATABASE
WHERE EXISTS (SELECT 1 FROM MY_TABLE);
При написании этого запроса используется несколько полезных (или бесполезных) уловок. Таблица RDB$DATABASE является системной таблицей, которая всегда имеет хотя бы одну запись.
Благодаря этому в вышеописанном запросе мы получим в качестве результата только одну запись, содержащую строку "таблица не пуста" в случае, если в таблице есть одна или более записей, что обуславливается SQL-оператором EXISTS.
Выбор константы обусловлен тем фактом, что для нас на этом этапе совершенно безразлично, что же именно содержится в таблице. Нам главное — знать, есть там записи или их нет.
Итерации по набору данных следует проводить с использованием свойства IsEmpty набора данных, а не путем перебора записей по какому-либо из счетчиков (об этом уже говорилось выше):
while not MyTable.Eof do begin
DoSomething;
MyTable.Next;
end;
Создайте в своем приложении также некий ProgressBar для отображения обработки больших объемов данных, чтобы пользователь не гадал, работает программа или уже повисла из-за ошибки; чтобы он мог знать, сколько еще времени займет та или иная операция.
Конечно, это не является альтернативой подсчету записей, но позволяет сделать процесс их обработки более эффективным путем разбиения на логические части меньшего объема. Как это можно сделать, будет показано на примере далее.