20.8.21

Повреждено - значит уничтожено

 Опять я про ремонт. Никак люди с битыми базами не могут понять, что "починить базу данных" - это не восстановить абсолютно всё. Это привести ее в рабочее состояние, не более того (а рабочее состояние - это прохождение штатных backup/restore).

Но те данные (записи), которые были повреждены - их восстановить невозможно, никак, абсолютно.
Например, у вас есть 5 печатных листов текста. И один лист потеряли (сгорел, в шреддере, и т.д.). Как вы "почините" потерянный лист? Ага, только набив этот текст заново! Причем, его же надо еще как-то помнить. А если не помните, то всё.

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

Допустим, есть справочник компаний CLIENTS, и таблица заказов ORDERS. Если повреждена ORDERS, то можно перевбить пропавшие записи с бумажных или ЭДО документов.
А если повредилась таблица CLIENTS? Тут хуже. Потому что backup пройдет, а restore - нет. Т.е. restore пройдет, но при активации индекса по столбцу связи выдаст ошибку (что отсутствуют записи в CLIENTS, на которые есть ссылающиеся записи в ORDERS), индекс не будет построен, ограничение целостности не будет работать, и будут тормоза с запросами, которые использовали такой индекс раньше.

Как это победить? Для начала, нужно найти потерянные в CLIENTS записи. Например
SELECT C.ID, O.CL_ID, <другие нужные столбцы>
FROM ORDERS O LEFT JOIN CLIENTS C
ON O.CL_ID = C.ID
WHERE C.ID IS NULL

То есть - нам нужно вытащить ВСЕ записи из таблицы ORDERS, т.к. там есть заказы, для которых клиенты потеряны. И потом оставить только те, которых не нашлось в CLIENTS - WHERE C.ID IS NULL.
Поскольку индекс по FK (ORDERS.CL_ID) активирован быть не может, для ускорения можно создать просто индекс по этому столбцу вручную. А затем уже выполнить запрос.
В противном случае на больших объемах данных запрос будет выполняться крайне долго.

Что дальше? Дальше мы можем вручную создать недостающие записи в CLIENTS, с минимумом информации - достаточно заполнить только ID и те столбцы, которые not null. Правда, если записей больше 10-20, то вручную это делать уже проблематично, и придется как-то автоматизировать.
Например, вместо SELECT C.ID, O.CL_ID в запросе выше написать
SELECT 'INSERT INTO ORDERS (ID) VALUES ('||O.CL_ID||');'

Добавили записи, убрали лишний индекс по CL_ID, активировали индекс по FK (напомню, это просто - ALTER INDEX indexname ACTIVE). И дальше уже можно по печатным или ЭДО документам заполнять информацию по клиентам.

Долго, нудно? Да! Надо было чаще бэкапы делать.