【问题标题】:Create unique string of first and last name in Oracle在 Oracle 中创建唯一的名字和姓氏字符串
【发布时间】:2014-01-23 00:37:08
【问题描述】:

我可以以编程方式执行此操作,但正在寻找更清洁的解决方案。

假设我有下表:

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

我想用以下规则创建一个独特的

  • 如果姓氏已经是唯一的,则只返回姓氏
  • 如果相同的姓氏返回第一个首字母(或更多),后跟一个句点,然后是姓氏

对于 Albert Smith,我会返回 Alb.Smith
对于查尔斯·约翰逊,我会返回 约翰逊
对于 Christion Roberts,我会返回 Christ.Roberts

是否有人对如何直接在 Oracle SQL 语句中完成此操作有任何想法,还是我应该坚持在程序中执行此操作?

【问题讨论】:

  • 很有趣,但我很好奇你会如何编程。如果编程是可能的,SQL 也是可能的。例如,如果有两个“Robert Haddock”和一个“Rob Haddock”,并且默认情况下您会修剪最后 3 个字母,或者系统将如何制作昵称?
  • 如果两个名字相同,那么我想我会返回 Robert Haddock,不带句号。如果我想要 Rob Haddock,它只会返回 Rob Haddock,因为那是他的全名。所以,是的,我想我需要更多规则......以编程方式,它目前重新查询表,将一个字母添加到第一个名称,直到它只返回。尽量避免多次sql调用。
  • 我了解您的编程方法,请您添加更多示例数据和预期输出。这将为您提供更好的解决方案。

标签: oracle plsql


【解决方案1】:

带有recursive subquery refactoring (CTE) 的版本,需要 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                  

下一个 CTE,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 

第三个CTEv只看唯一值,然后根据唯一值的长度进行排序;因此,在所有记录中唯一的记录的名字的最短缩写被列为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),这似乎是你想要的评论。

不确定这是否真的符合“更清洁”的条件,除了它只命中原始表一次。

【讨论】:

  • 非常酷。我将不得不绕着这个头绕一圈。它似乎确实满足了所有要求,因此我将其标记为答案。
【解决方案2】:

试试这个:

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

【讨论】:

  • 但这并没有给出唯一值;你有两个A.Smith 和两个C.Roberts
  • 这可行,但仅用于在需要时附加第一个首字母。它不会更深入。
猜你喜欢
  • 2013-01-07
  • 2020-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多