Удаление записей в таблице

Утилиты

Таблицы FoxPro — это прямые наследники формата DBASE. В этом формате процесс удаления записей разбит на 2 этапа. Сначала записи помечаются как удаленные, но физически все еще сохраняются в таблице. А для их физического удаления необходимо дать специальную команду. Причем физическое удаление требует эксклюзивного (единоличного) доступа к таблице.

Под термином «удаление» в FoxPro понимается именно установка метки на удаление, а не физическое удаление записи в таблице. Т.е. команды DELETE, DELETE-SQL физически не удаляют записи; триггер DELETE срабатывает при установке метки на удаление; триггер INSERT срабатывает при снятии метки на удаление (ну, и при физическом создании новой записи)

Чтобы записи, помеченные как удаленные, не отображались при работе с таблицами, используют специальную глобальную настройку
SET DELETED ON

Здесь несколько «нелогичная» настройка: ON — прячет записи, помеченные как удаленные (учитывает такие записи), а OFF — наоборот, отображает такие записи (игнорирует, не учитывает такие записи).

Следует помнить, что при использовании Private DataSession настройка SET DELETED сбрасывается в значение по умолчанию (OFF). Т.е. при открытии Private DataSession необходимо позаботиться о корректной настройке среды данных.

Для физического удаления записей ранее помеченных как удаленные, используется специальная команда «PACK». Следует помнить, что в отличие от команд на удаление (DELETE, DELETE-SQL) данная команда требует открытия таблицы в режиме EXCLUSIVE. Что, как правило, недопустимо при работе в сетевом режиме. Для преодоления этого противоречия используется одна из 2 стратегий программирования:

При создании новых записей использовать записи ранее помеченные как удаленные
Вынести выполнение команды PACK в специальные служебные процедуры по обслуживанию базы данных

При создании новых записей использовать записи ранее помеченные как удаленные

В принципе, тут возможны два варианта решения: восстанавливать ранее удаленные записи (командой RECALL) или вместо удаления записей по команде DELETE менять содержимое специально созданного для этой цели поля, как раз и определяющего — удалена запись или нет.

В любом случае, данная стратегия предполагает отказ от целого ряда команд и функций которые так или иначе могут автоматически создавать новые записи (APPEN BLANK, APPEND FROM, INSERT-SQL, буферизация, обновляемые Local View и т.п.). А создание новых записей осуществлять через специальную функцию, которая сначала ищет запись, помеченную как удаленная, и использует ее как новую запись.

К недостаткам данного способа следует еще отнести относительную сложность процедуры вставки новой записи. Тут недостаточно сделать просто LOCATE + RECALL. Все несколько сложнее. Достаточно много нюансов блокировки при работе в многопользовательском режиме.

Попробуйте, например, прикинуть какие потребуются действия, чтобы при создании новой записи «одновременно» двумя пользователями не произошло затирание информации, введенной одним из пользователей. Т.е. два пользователя «одновременно» сделали LOCATE, но RECALL естественно сделал только один из них. Как заставить другого пользователя отказаться от притязаний на обладание найденной записью в пользу первого, сделавшего RECALL? Разумеется, задача решаемая. Я просто хочу показать, что она достаточно не тривиальна.

Кроме того, данная стратегия фактически запрещает использовать Memo-поля. Почему? Да потому, что при работе с Memo-полями время от времени необходимо давать команду PACK. Это связано с особенностью работы с memo-полями. Подробнее об этих особенностях читайте в разделе «Типы данных». Ну а если все равно приходится давать команду PACK, то какой смысл во всех этих сложностях?

Итого, получается, что использование данной стратегии накладывает значительные ограничения на программиста.

После всех этих рассуждений, возникает вопрос, который следовало бы задать с самого начала. А когда реально нет возможности выполнить команду PACK?

Нет возможности выполнить команду PACK, когда приложение работает в многопользовательском режиме и невозможно его остановить даже на профилактику, хотя бы на полчаса раз в год.

Разумеется, такие задачи тоже существуют. Но! Если у Вас стоит именно такая задача, то выбор в качестве хранилища данных таблиц DBASE — это весьма странный выбор. Дело тут не в том, что такую задачу сложно решить при помощи таблиц DBASE, а в том, что такие задачи предъявляют повышенные требования, как к надежности, так и к объему (количеству записей) базы данных. Т.е. в большинстве случаев требования таких задач заведомо превышают физические возможности формата хранения данных в таблицах DBASE.
Вынести выполнение команды PACK в специальные служебные процедуры по обслуживанию базы данных

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

Казалось бы, есть более простое решение: давать команду PACK при каждом входе (выходе) из программы. Однако это лишь кажущаяся простота.

Если Вы разрабатываете программу для одного пользователя, то в принципе такая стратегия оправдана. Но при работе в сетевом (многопользовательском) режиме данная стратегия приведет к тому, что часть пользователей просто не сможет с первого раза войти в программу, поскольку в этот момент другой пользователь будет «держать» таблицы в режиме EXCLUSIVE.

Кроме того, нет особой необходимости физически удалять записи, помеченные как удаленные, непосредственно в момент (или в том сеансе данных) когда произошла установка метки на удаление. При наличии небольшого количества записей помеченных как удаленные скорость работы (выполнения запросов, поиск данных) упадет незначительно. Ну, будет немного «мусора», ну и что?

Т.е. необходимость в физическом удалении записей ранее помеченных как удаленные возникает только тогда, когда их количество превышает некоторый предел. В результате, приложение начинает работать заметно медленнее — заметно для пользователя. Оценить этот предел достаточно проблематично, поскольку он зависит от многих факторов. Поэтому логично просто безо всяких проверок и оценок с определенной периодичностью (раз в месяц, раз в квартал, раз в полгода) запускать процедуру очистки базы данных. Периодичность запуска этой процедуры зависит от интенсивности удаления записей в Вашей программе.

Еще замечу, что если открыть в режиме EXCLUSIVE собственно базу данных, то другой пользователь не сможет открыть ни одну из таблиц базы данных. Это упрощает создание такой процедуры очистки базы данных.

Вывод однозначен. Использование специальной процедуры очистки базы данных от записей помеченных как удаленные предпочтительнее во всех смыслах перед повторным использованием ранее удаленных записей.
Следует ли создавать индекс по выражению Deleted()

О чем вообще речь? Дело в том, что для ускорения выборок данных в FoxPro используется специальная технология, называемая Rushmore — оптимизация. Что это такое в данном случае не важно. Важным является тот факт, что данная технология для ускорения выборки использует индексы. Без индексов она просто не работает.

Так вот, факт наличия удаленных записей в таблице хотя и отсекается настройкой SET DELETED ON, но, тем не менее, влияет на факт оптимизации запроса. По существу, настройка SET DELETED ON — это специфический фильтр, накладываемый на таблицу или дополнительное (неявное) условие выборки записей. Ну, а раз есть условие, но по нему нет индекса, то это приводит к снижению уровня Rushmore-оптимизации.

В связи с этим, в литературе советуют создавать индекс по выражению Deleted() примерно такого видаINDEX ON Deleted() TAG Udal

Так вот, несмотря на то, что создание такого индекса приведет к улучшению Rushmore — оптимизации, но в подавляющем большинстве случаев это приведет к замедлению получения выборки. Подробнее о причинах такого поведения читайте в разделе Сколько и каких индексов надо создавать

Т.е. я бы НЕ рекомендовал Вам создавать подобный индекс.
Команда ZAP

Для физического удаления записей в таблице существует еще одна команда: ZAP. Данная команда предназначена для физического удаления вообще всех записей таблицы. Т.е. ее использование по результатам действия эквивалентно такой последовательности команд:
select MyTab
DELETE ALL
PACK

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

Так вот, команда ZAP не вызывает срабатывание триггеров на удаление. Просто удаляет все записи таблицы без каких-либо проверок. В связи с такой особенностью, FoxPro при использовании данной команды обязательно попросит подтвердить Ваше решение удалить все записи. Подавить выдачу такого системного подтверждения, можно сделав настройку SET SAFETY OFF.

Также как и команда PACK, команда ZAP требует открытия таблицы в режиме EXCLUSIVE.
Хранимые процедуры

Хранимые процедуры — это те процедуры, которые хранятся в контейнере базы данных.

Под словом «хранятся» здесь понимается именно физическое расположение. Т.е. хранимые процедуры физически расположены в контейнере базы данных.

Если интересно, где именно они хранятся, то можете открыть базу данных как таблицу и увидеть, что текст «хранимых процедур» хранится в Memo-поле «Code». В строке со значением поля ObjectName = «StoredProceduresSource» исходный текст «хранимых процедур», а в строке со значением поля ObjectName = «StoredProceduresObject» — откомпилированный текст. В принципе, если Вы желаете скрыть от слишком «продвинутого» пользователя исходный текст «хранимых процедур», то Вы можете смело очистить содержимое поля Code для строки со значением поля ObjectName = «StoredProceduresSource», поскольку в процессе работы используется не исходный, а откомпилированный текст «хранимых процедур».

«Хранимые процедуры» становятся доступны в момент открытия контейнера базы данных, а контейнер базы данных автоматически открывается при открытии любой таблицы включенной в базу данных. Таким образом, «Хранимые процедуры» можно использовать сразу после открытия таблицы без каких-либо дополнительных команд типа SET PROCEDURE.

Для чего нужны хранимые процедуры

А зачем вообще понадобилось хранить какие-то процедуры внутри контейнера базы данных? Разве недостаточно использовать обычные процедурные файлы?

Причина здесь в том, что требуется обеспечить доступ к определенным процедурам вне зависимости от того, запущено приложение или нет. Например, если была создана некоторая процедура, которая проверяет допустимость операции создания новой записи (триггер на вставку), то эта процедура должна быть запущена, в момент попытки вставить новую запись, вне зависимости от того запущено ли соответствующее приложение или нет. В противном случае, например, на этапе отладки, Вы рискуете серьезно нарушить целостность и непротиворечивость Вашей базы данных.

Исходя из данного предназначения хранимых процедур, становится ясно, какие именно процедуры и функции стоит в ней хранить, а какие нет.
Если процедура или функция обеспечивает целостность и непротиворечивость данных вне зависимости от приложения, то такую процедуру или функцию следует хранить в «Хранимых процедурах»

Определение получилось несколько мудреное. Если сказать то же самое проще, то в «Хранимых процедурах» должны хранится те процедуры и функции, вызов которых прописывается в свойствах таблиц или контейнера базы данных. Имеется в виду, что когда Вы вызываете режим модификации таблицы, то там есть ряд мест, где можно написать вызов функции (Rule, Triggers, Default). Вот те функции, которые там вызываются и должны быть записаны в «хранимых процедурах».

«Хранимые процедуры» — это обычные процедуры и функции FoxPro. Т.е. из них может быть организован вызов других процедур и функций. Так вот, эти «подчиненные» процедуры и функции также должны хранится внутри «хранимых процедур». Вообще, крайне нежелательно изнутри «хранимых процедур» обращаться во вне базы данных. Ведь основное назначение «хранимых процедур» — это обеспечение некоторых действий вне зависимости от «внешнего мира»

Особенности работы с хранимыми процедурами

Поскольку физически «хранимые процедуры» хранятся в Memo-полях, то в связи с определенными особенностями по работе с Memo-полями после внесения исправлений в «хранимые процедуры» следует выполнить очистку базы данных для уменьшения размера файла DCT и удаления неиспользуемого пространства.

«Хранимые процедуры» можно выгрузить в текстовый файл командой COPY PROCEDURES и соответственно загрузить командой APPEND PROCEDURES. Т.е. в принципе, можно динамически добавлять/удалять хранимые процедуры, хотя я не советовал бы заниматься этим новичкам. Слишком велик риск, разрушить все правила целостности базы данных.

Список имен всех «хранимых процедур» текущей базы данных можно получить по команде DISPLAY PROCEDURES (или LIST PROCEDURES)