Загрузка данных
with src as (
select naimen,
trim(
regexp_replace(
-- сначала отрезаем номера счетов, потом хвост реквизитов
regexp_replace(trim(naimen), '[[:space:]]*[0-9]{9,}.*$', ''),
'[[:space:]]+(' ||
'[РP]/[СC]' ||
'|([[:alpha:]]+[[:space:]]+)?ОСБ' ||
'|В[[:space:]]+(ОПЕРУ|ПУШКИНСК)' ||
'|ТРАНЗИТНЫЙ|ДОП\.|НЕВЫЯСНЕНН' ||
'|Ф/АЛ|ФИЛИАЛ|КАШИРСКИЙ' ||
'|Г\.[[:space:]]*[[:alpha:]]' ||
'|ОС$|Р/?$' ||
').*$',
'', 1, 1, 'i')
) as clean_name
from sh.uch_vse
where naimen is not null
),
norm as (
select naimen,
clean_name,
upper(clean_name) as r, -- для LLC, LTD и т.п.
-- верхний регистр, один пробел, латиница и 0 -> кириллица
translate(
regexp_replace(upper(clean_name), '[[:space:]]+', ' '),
'ABCEHKMOPTXY0',
'АВСЕНКМОРТХУО'
) as n
from src
),
flag as (
select naimen,
clean_name,
-- 1 = название обрезано полем (длина 50) и хвост не вырезался
case when length(naimen) >= 50
and clean_name = trim(naimen) then 1 else 0 end as is_cut,
regexp_replace(n, '[^А-ЯЁA-Z0-9]', '') as k,
case when length(clean_name) >= 5
-- ИП не считаем
and not regexp_like(n,
'^[^А-ЯЁA-Z0-9]*(ИП|ЧП|ИНДИВИДУАЛЬНЫЙ ПРЕДПРИНИМАТЕЛЬ)([^А-ЯЁA-Z0-9]|$)')
and (
-- 1) однозначные формы: в любом месте строки
regexp_like(n,
'(^|[^А-ЯЁA-Z0-9])(ООО|ОАО|ЗАО|ПАО|НАО|ТОО|ФГУП|МУП|ГУП|ГБУ|ГКУ|ГАУ|АНО|НКО|СПК|КФХ|РПО|СНТ|ТСЖ|ЖСК)([^А-ЯЁA-Z0-9]|$)')
-- 2) короткие неоднозначные: только в начале или в конце
or regexp_like(n,
'^[^А-ЯЁA-Z0-9]*(АО|ПК|КБ|АКБ)([^А-ЯЁA-Z0-9]|$)')
or regexp_like(n,
'(^|[^А-ЯЁA-Z0-9])(АО|ПК|КБ|АКБ)[^А-ЯЁA-Z0-9]*$')
-- 3) полные названия (в т.ч. обрезанные)
or regexp_like(n,
'ОБЩЕСТВО С ОГРАНИЧЕННОЙ|АКЦИОНЕРНОЕ ОБЩЕСТВО|УНИТАРНОЕ ПРЕДПРИЯТИЕ')
-- 4) иностранные формы
or regexp_like(r,
'(^|[^A-ZА-ЯЁ0-9])(LLC|LTD|JSC|PJSC|INC|GMBH)([^A-ZА-ЯЁ0-9]|$)')
-- 5) название в кавычках (кроме физлиц с инициалами)
or (regexp_like(clean_name,
'["«»„“”`<>''].{2,}["«»„“”`<>'']')
and not regexp_like(clean_name,
'^[А-ЯЁ][А-ЯЁа-яё-]+[[:space:]]+[А-ЯЁ]\.', 'c'))
)
then 1 else 0 end as is_org
from norm
),
cand as (
select /*+ materialize */ clean_name, is_cut, k
from flag
where is_org = 1
)
select min(c.clean_name) as "Наименование"
from cand c
where c.is_cut = 0
or not exists (
select 1
from cand x
where x.k like c.k || '_%' -- есть более длинная запись с тем же началом
)
group by c.k
order by 1;
with src as (
select naimen,
trim(
regexp_replace(
regexp_replace(trim(naimen), '[[:space:]]*[0-9]{9,}.*$', ''),
'[[:space:]]+(' ||
'[РP]/[СC]' ||
'|([[:alpha:]]+[[:space:]]+)?ОСБ' ||
'|В[[:space:]]+(ОПЕРУ|ПУШКИНСК)' ||
'|ТРАНЗИТНЫЙ|ДОП\.|НЕВЫЯСНЕНН' ||
'|Ф/АЛ|ФИЛИАЛ|КАШИРСКИЙ' ||
'|Г\.[[:space:]]*[[:alpha:]]' ||
'|ОС$|Р/?$' ||
').*$',
'', 1, 1, 'i')
) as clean_name
from sh.uch_vse
where naimen is not null
),
norm as (
select naimen,
clean_name,
upper(clean_name) as r,
translate(
regexp_replace(upper(clean_name), '[[:space:]]+', ' '),
'ABCEHKMOPTXY0',
'АВСЕНКМОРТХУО'
) as n
from src
),
flag as (
select naimen,
case when length(clean_name) >= 5
and not regexp_like(n,
'^[^А-ЯЁA-Z0-9]*(ИП|ЧП|ИНДИВИДУАЛЬНЫЙ ПРЕДПРИНИМАТЕЛЬ)([^А-ЯЁA-Z0-9]|$)')
and (
regexp_like(n,
'(^|[^А-ЯЁA-Z0-9])(ООО|ОАО|ЗАО|ПАО|НАО|ТОО|ФГУП|МУП|ГУП|ГБУ|ГКУ|ГАУ|АНО|НКО|СПК|КФХ|РПО|СНТ|ТСЖ|ЖСК)([^А-ЯЁA-Z0-9]|$)')
or regexp_like(n,
'^[^А-ЯЁA-Z0-9]*(АО|ПК|КБ|АКБ)([^А-ЯЁA-Z0-9]|$)')
or regexp_like(n,
'(^|[^А-ЯЁA-Z0-9])(АО|ПК|КБ|АКБ)[^А-ЯЁA-Z0-9]*$')
or regexp_like(n,
'ОБЩЕСТВО С ОГРАНИЧЕННОЙ|АКЦИОНЕРНОЕ ОБЩЕСТВО|УНИТАРНОЕ ПРЕДПРИЯТИЕ')
or regexp_like(r,
'(^|[^A-ZА-ЯЁ0-9])(LLC|LTD|JSC|PJSC|INC|GMBH)([^A-ZА-ЯЁ0-9]|$)')
or (regexp_like(clean_name,
'["«»„“”`<>''].{2,}["«»„“”`<>'']')
and not regexp_like(clean_name,
'^[А-ЯЁ][А-ЯЁа-яё-]+[[:space:]]+[А-ЯЁ]\.', 'c'))
)
then 1 else 0 end as is_org
from norm
)
select distinct naimen as "Наименование"
from flag
where is_org = 0
order by 1;
create table spr_gorod as
with src as (
select -- латинские двойники -> кириллица, чтобы поймать "Г.МOСКВА" со смешанной раскладкой
translate(upper(trim(naimen)), 'ABCEHKMOPTXY', 'АВСЕНКМОРТХУ') as u,
length(naimen) as len
from sh.uch_vse
where naimen is not null
),
ext as (
select u, len,
coalesce(
-- двусловные города, где первое слово из списка (список можно дополнять)
regexp_substr(u,
'(^|[^А-ЯЁ])(Г|ГОР|ГОРОД|ГОРОДА)[. ]+((НИЖНИЙ|НОВЫЙ|НОВАЯ|СТАРЫЙ|СТАРАЯ|ВЕЛИКИЙ|ВЕЛИКИЕ|НАБЕРЕЖНЫЕ|УСТЬ|ЙОШКАР)[ -][А-ЯЁ]{2,})',
1, 1, 'c', 3),
-- обычные: одно слово или через дефис (САНКТ-ПЕТЕРБУРГ, РОСТОВ-НА-ДОНУ)
regexp_substr(u,
'(^|[^А-ЯЁ])(Г|ГОР|ГОРОД|ГОРОДА)[. ]+([А-ЯЁ]{2,}(-[А-ЯЁ]{1,}){0,3})',
1, 1, 'c', 3)
) as raw
from src
),
clean as (
select regexp_replace(replace(raw, 'Ё', 'Е'), '[[:space:]]+', ' ') as city
from ext
where raw is not null
-- отбрасываем город, если строка обрезана полем на самом городе (Г.МОСК)
and not (len >= 50
and regexp_like(u, '(^|[^А-ЯЁ])(Г|ГОР|ГОРОД|ГОРОДА)[. ]+[А-ЯЁ -]*$'))
),
uniq as (
select city
from clean
where length(city) >= 3
group by city
-- having count(*) >= 2 -- раскомментируйте, чтобы убрать редкий мусор
)
select row_number() over (order by city) as id,
city as name
from uniq;
-- проверка с частотами (редкие значения обычно мусор или опечатки):
-- select city, count(*) cnt from ( ...тот же clean... ) group by city order by cnt desc;