2014-01-22 2 views
2

Я могу сделать это программно, но искал более чистого решения.Создайте уникальную строку имени и фамилии в Oracle

Допустим, у меня есть следующая таблица:

First Name  Last Name 
Smith   Albert  
Smith   Alphonse  
Smith   Jason   
Johnson   Charles 
Roberts   Chris 
Roberts   Christian 

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

  • Если фамилия уже уникален только возвращал только последнее имя
  • Если такая же фамилия возвращает сначала первоначальную (или более), а затем период, а затем фамилию

Для Альберта Смита я хотел бы вернуться Alb.Smith
Для Чарльза Джонсона я вернусь Джонсон
Для Christion Робертс Я хотел бы вернуться Christ.Roberts

Кто-нибудь есть какие-либо мысли о том, как это сделать непосредственно в Oracle SQL-заявлении или я должен придерживаться этого в программе?

+0

Интересно, но мне любопытно, как вы это сделаете программным. Если программная возможность возможна, SQL также возможен. Например, что, если есть два «Роберта Хаддока» и «Роб Хэддок», и по умолчанию вы обрезаете последние 3 буквы или как система будет создавать ник? –

+0

Если два с тем же именем, то, я думаю, я просто вернусь к Роберту Хаддоку без периода. Если бы я хотел Роба Хэддока, он просто вернул Роба Хэддока, так как это его полное имя. Так что да, я думаю, мне нужно больше правил ... программно, он в настоящее время повторно запрашивает таблицу, добавляя одну букву к первому имени, пока не получит только возврат. Попытка избежать множественных вызовов sql. – jbrook10

+0

Я получаю ваш программный подход. Не могли бы вы добавить еще некоторые данные образца и ожидаемый результат. Это поможет вам найти лучшие решения. –

ответ

6

Версия с recursive subquery refactoring (КТР), которая требует 11gR2:

with t (last_name, first_name, orig_rn, part, part_length, remaining) as (
    select last_name, first_name, 
    row_number() over (order by last_name, first_name), 
    cast (null as varchar2(20)), 0, length(first_name) 
    from t42 
    union all 
    select last_name, first_name, orig_rn, 
    part || substr(first_name, part_length + 1, 1), 
    part_length + 1, 
    remaining - 1 
    from t 
    where remaining > 0 
), 
u as (
    select last_name, first_name, orig_rn, part, part_length, 
    count(distinct orig_rn) over (partition by last_name) as last_name_count, 
    count(distinct orig_rn) over (partition by last_name, part) as part_count 
    from t 
), 
v as (
    select last_name, first_name, orig_rn, part, last_name_count, 
    row_number() over (partition by orig_rn order by part_length) as rn 
    from u 
    where (part_count = 1 or part = first_name) 
) 
select case when last_name_count = 1 then null 
    when part = first_name then first_name || ' ' 
    else part || '. ' 
    end || last_name as condendsed_name 
from v 
where rn = 1 
order by orig_rn; 

Что дает:

CONDENSED_NAME        
---------------------------------------------- 
Johnson           
Chris Roberts         
Christ. Roberts         
Alb. Smith          
Alp. Smith          
J. Smith          

SQL Fiddle.

t CTE рекурсивный. Она начинается с исходными строками таблицы и создает дополнительные строки для каждого возможного сжатия первого имени:

with t (last_name, first_name, orig_rn, part, part_length, remaining) as (
    select last_name, first_name, 
    row_number() over (order by last_name, first_name), 
    cast (null as varchar2(20)), 0, length(first_name) 
    from t42 
    union all 
    select last_name, first_name, orig_rn, 
    part || substr(first_name, part_length + 1, 1), 
    part_length + 1, 
    remaining - 1 
    from t 
    where remaining > 0 
) 
select last_name, first_name, part 
from t 
where last_name = 'Johnson' 
order by orig_rn, part_length; 

LAST_NAME   FIRST_NAME   PART     
-------------------- -------------------- ------------------------ 
Johnson    Charles          
Johnson    Charles    C       
Johnson    Charles    Ch      
Johnson    Charles    Cha      
Johnson    Charles    Char      
Johnson    Charles    Charl      
Johnson    Charles    Charle     
Johnson    Charles    Charles     

Следующей КТР, u (да, извините об именах, я невдохновленный) сравнивают значения по всем строк и подсчитывает вхождения. Все, что имеет значение 1, уникально.

... 
u as (
    select last_name, first_name, orig_rn, part, part_length, 
    count(distinct orig_rn) over (partition by last_name) as last_name_count, 
    count(distinct orig_rn) over (partition by last_name, part) as part_count 
    from t 
) 
select last_name, first_name, part, last_name_count, part_count 
from u 
where last_name = 'Roberts' 
order by orig_rn, part_length; 

LAST_NAME   FIRST_NAME   PART      LAST_NAME_COUNT PART_COUNT 
-------------------- -------------------- ------------------------ --------------- ---------- 
Roberts    Chris              2   2 
Roberts    Chris    C          2   2 
Roberts    Chris    Ch          2   2 
Roberts    Chris    Chr         2   2 
Roberts    Chris    Chri         2   2 
Roberts    Chris    Chris         2   2 
Roberts    Christian             2   2 
Roberts    Christian   C          2   2 
Roberts    Christian   Ch          2   2 
Roberts    Christian   Chr         2   2 
Roberts    Christian   Chri         2   2 
Roberts    Christian   Chris         2   2 
Roberts    Christian   Christ         2   1 
Roberts    Christian   Christi        2   1 
Roberts    Christian   Christia        2   1 
Roberts    Christian   Christian        2   1 

Третий КТР v смотрит только на уникальные из них, а затем ранжирует их по длине уникального значения; поэтому кратчайшее сокращение первого имени для записи, которая является уникальной для всех записей, оценивается как 1.

... 
v as (
    select last_name, first_name, orig_rn, part, last_name_count, 
    row_number() over (partition by orig_rn order by part_length) as rn 
    from u 
    where (part_count = 1 or part = first_name) 
) 
select last_name, first_name, part, last_name_count 
from v 
where rn = 1 
order by orig_rn; 

LAST_NAME   FIRST_NAME   PART      LAST_NAME_COUNT 
-------------------- -------------------- ------------------------ --------------- 
Johnson    Charles              1 
Roberts    Chris    Chris         2 
Roberts    Christian   Christ         2 
Smith    Albert    Alb         3 
Smith    Alphonse    Alp         3 
Smith    Jason    J          3 

Затем окончательный запрос извлекает только тот ранг 1, которые являются кратчайшими уникальными значениями, и форматирует их так, как вы хотели.

Если два человека имеют одно и то же имя, то оба из них полностью изложены (demo), которые, как представляется, вы хотите получить от вашего комментария.

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

+0

Очень круто. Мне придется немного обмануть голову. Мне кажется, это все требования, поэтому я буду отмечать это как ответ. – jbrook10

+0

OMG !! Ты жжешь!! –

3

Попробуйте это:

with 
last_names as (
    select last_name, count(*) as last_name_count 
    from table_name 
    group by last_name) 

select case 
     when b.last_name_count = 1 then a.last_name 
     else substr(a.first_name,1,1)||'. '||a.last_name 
     end as name 
from table_name a 
join last_names b 
on a.last_name = b.last_name; 

замена table_name с правильным именем.

+0

Для тестирования [SQL Fiddle] (http://www.sqlfiddle.com/#!4/e752b/1) –

+0

Это не дает уникальных значений; у вас есть два 'A.Smith' и два' C.Roberts'? –

+0

Это работает, но только для того, чтобы при необходимости добавить первый исходный код. Это не идет глубже. – jbrook10

Смежные вопросы