Найти в Дзене
Oracle APEX

Страдаете ли вы извращениями? - Что вы, я ими наслаждаюсь!

Очередное использование базы для изящного решения небазовой задачи.

Есть совокупность файлов. Например, версии файлов документов от разных дат. Формат имени файла - так уж дан -

<номер документа>-<дата версии>-<под-номер документа>.<расширение>

- что-то типа

12345-050520-2.xls

Надо удалить все версии, кроме последней по дате.

Как подойдет к задаче программист? Станет разбирать циклом строку и "всплывать пузырьком". Как подойдет базист? Как-то так...

Во-первых, он создаст таблицу, что-то типа:

create table maninblack_tmp_tab (filename varchar2(4000))
;

- и будет записывать в нее скан папки, подлежащей прореживанию. При этом - внимание! это Oracle! - транзакцию оставит висеть, т.е. ни commit'а, ни rollback'а. Еще раз: это Oracle: по умолчанию транзакция висит - и пусть висит! Это значит, что никакие другие сессии внесенных в таблицу записей не увидят.

Во-вторых, он напишет запрос - именно запрос, а не скрипт, который проранжирует записанные в таблицу - и висящие на транзакции! - строки по выделенной из строки дате.

Сначала он выделит из - записанных в таблицу - компонентов имен файлов дату версии, номер документа и под-номер документа:

select file_name, to_date(regexp_replace(regexp_replace(file_name, '^[0-9]+\-', ''), '\-.+$', ''), 'mmddyy') as d
, regexp_substr(file_name, '^[0-9]+') as f
, regexp_replace(regexp_replace(file_name, '^[0-9]+\-[0-9]+\-', ''), '\.[a-zA-Z]+', '') as p
from maninblack_tmp_tab
;
...
10695-100115-1.xls 01.10.15 10695 1
10704-052515-1.xls 25.05.15 10704 1
10704-052515-2.xls 25.05.15 10704 2
10704-080415-2.xls 04.08.15 10704 2
...

Затем построит аналитической функцией ранг по возрастающей дате, секционированный по конкатенации номера документа и под-номера документа:

select file_name, rank() over (partition by f||p order by d) as rk
from
(
select file_name, to_date(regexp_replace(regexp_replace(file_name, '^[0-9]+\-', ''), '\-.+$', ''), 'mmddyy') as d
, regexp_substr(file_name, '^[0-9]+') as f
, regexp_replace(regexp_replace(file_name, '^[0-9]+\-[0-9]+\-', ''), '\.[a-zA-Z]+', '') as p
from maninblack_tmp_tab
)
;
...
10695-100115-1.xls 1
10704-052515-1.xls 1
10704-052515-2.xls 1
10704-080415-2.xls 2
...

А потом просто отфильтрует ранги больше 1 - на удаление:

select file_name from
(
select file_name, rank() over (partition by f||p order by d) as rk
from
(
select file_name, to_date(regexp_replace(regexp_replace(file_name, '^[0-9]+\-', ''), '\-.+$', ''), 'mmddyy') as d
, regexp_substr(file_name, '^[0-9]+') as f
, regexp_replace(regexp_replace(file_name, '^[0-9]+\-[0-9]+\-', ''), '\.[a-zA-Z]+', '') as p
from maninblack_tmp_tab
)
)
where rk > 1
;

И оформит запрос, выдающий список удаляемых файлов, хранимым представлением:

create view maninblack_del_vi as
select file_name from
...
where rk > 1
;

Соответственно, в коде, например, PHP, он сделает сканирование папки (scandir) и insert полученных имен файлов в выше описанную таблицу. Затем, не делая более ничего, он сделает select из описанного представления и циклическое удаление (unlink) файлов, имена которых получены из запроса к этому представлению.

А далее - внимание, магия! - он сделает просто rollback на коннекции с базой. Таблица останется - вечно! - пустой, а другие параллельные сессии ничего не увидят.

Это Oracle!