如果有人键入最后/第一/中间,如何将名称拆分为第一/中间/最后?已知订单

Posted

技术标签:

【中文标题】如果有人键入最后/第一/中间,如何将名称拆分为第一/中间/最后?已知订单【英文标题】:How to split names into first/middle/last if there are people who typed last/first/middle? Order known 【发布时间】:2019-06-15 21:00:46 【问题描述】:

我正在尝试根据指定的顺序将名称拆分为名字、中间名和姓氏。我不知道如何做到这一点,任何帮助将不胜感激。我正在使用 sql server 2008 工作。

我附上了一个示例数据集和我想创建的理想数据集。

ID  ORDER                   NAME
1   first, middle, last     Bruce, Batman, Wayne
2   middle, last, first     Superman, Kent, Clark
3   last, first, middle     Prince, Diana, Wonderwoman

进入:

ID  ORDER                   NAME
1   first                   Bruce
1   middle                  Batman
1   last                    Wayne
2   middle                  Superman
2   last                    Kent
2   first                   Clark
3   last                    Prince
3   first                   Diana
3   middle                  Wonderwoman

【问题讨论】:

你有哪个版本? 为什么不努力规范化您的数据,而不是进行另一种形式的非规范化? @larnu 一厢情愿地想,也许这个问题是因为正在进行标准化;) 考虑到你可以一次性标准化数据,我坐在“不太可能”的一边@CaiusJard :( 【参考方案1】:

SQL Server 没有很好的字符串处理功能。不过,您可以使用递归 CTE 来做到这一点:

with cte as (
      select id,
             convert(varchar(max), left(ord, charindex(',', ord) - 1)) as ord,
             convert(varchar(max), left(name, charindex(',', name) - 1)) as name,
             convert(varchar(max), stuff(ord, 1, charindex(',', ord) + 1, '')) as ord_rest,
             convert(varchar(max), stuff(name, 1, charindex(',', name) + 1, '')) as name_rest,
             1 as lev
      from t
      union all
      select id,
             convert(varchar(max), left(ord_rest, charindex(',', ord_rest + ',') - 1)) as ord,
             convert(varchar(max), left(name_rest, charindex(',', name_rest + ',') - 1)) as name,
             convert(varchar(max), stuff(ord_rest, 1, charindex(',', ord_rest + ',') + 1, '')) as ord_rest,
             convert(varchar(max), stuff(name_rest, 1, charindex(',', name_rest + ',') + 1, '')) as name_rest,
             lev + 1
      from cte
      where ord_rest <> '' and lev < 10
     )
select id, ord, name
from cte
order by id, lev

Here 是一个 dbfiddle。

【讨论】:

【参考方案2】:

在返回序列的解析/拆分函数的帮助下,使用 CROSS APPLY 就变成了一件小事

示例

Select A.ID 
      ,B.*
 From  YourTable A
 Cross Apply (
                Select [Order] = B1.RetVal
                      ,[Name]  = B2.RetVal
                 From  [dbo].[tvf-Str-Parse]([ORDER],',') B1
                 Join  [dbo].[tvf-Str-Parse]([NAME] ,',') B2 on B1.RetSeq=B2.RetSeq
             ) B

退货

ID  Order   Name
1   first   Bruce
1   middle  Batman
1   last    Wayne
2   middle  Superman
2   last    Kent
2   first   Clark
3   last    Prince
3   first   Diana
3   middle  Wonderwoman

感兴趣的功能

CREATE FUNCTION [dbo].[tvf-Str-Parse] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (  
    Select RetSeq = row_number() over (order by 1/0)
          ,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
    From  ( values (cast('<x>' + replace((Select replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.'))) as A(x)
    Cross Apply x.nodes('x') AS B(i)
);

【讨论】:

谢谢!它解决了我的大部分问题,并帮助我弄清楚下一步需要做什么。 @TinaChan 很高兴为您提供帮助。【参考方案3】:

我发现其他答案有点难以理解 - 它们肯定是巧妙的技巧,但我认为任何来维护它们的人可能会像“whaaaat?”。在这里,我计算出第一个 cte 中逗号的索引(第一个逗号字符串索引进入 o1/n1,第二个逗号进入 o2/n2),将字符串切开(1 和第一个逗号之间的子字符串,第一个之间的子字符串和第二个逗号,第三个逗号后的子字符串)在第二个 cte 中,然后使用几个联合将结果从 7 列转换为 3

WITH idxs AS
(
SELECT 
  id,
  order,
  name,
  CHARINDEX(',', [order]) as o1,
  CHARINDEX(',', [order], CHARINDEX(',', [order]) + 1) as o2,
  CHARINDEX(',', name) as n1,
  CHARINDEX(',', name, CHARINDEX(',', name) + 1) as n2
FROM
  t
),
cuts as (
SELECT
  id,
  SUBSTRING([order], 1, o1-1) as ord1,
  SUBSTRING([order], o1+1, o2-o1-1) as ord2,
  SUBSTRING([order], o2+1, 4000) as ord3,
  SUBSTRING(name, 1, n1-1) as nam1,
  SUBSTRING(name, n1+1, n2-n1-1) as nam2,
  SUBSTRING(name, n2+1, 4000) as nam3
FROM
  idxs
)

SELECT id, ord1 as [order], nam1 as name FROM cuts
UNION ALL
SELECT id, ord2, nam2 FROM cuts
UNION ALL
SELECT id, ord3, nam3 FROM cuts

请注意,如果您的数据有时有空格,有时没有,您将受益于在输出中使用 LTRIM/RTRIM

如果逗号后始终存在空格,您还可以调整子字符串索引以删除空格(任何起始索引为 x+1 将是 x+2,因此长度必须为 -2)

【讨论】:

以上是关于如果有人键入最后/第一/中间,如何将名称拆分为第一/中间/最后?已知订单的主要内容,如果未能解决你的问题,请参考以下文章

将泰国名字解析为第一个最后一个

BxSlider 将最后一张幻灯片显示为第一张幻灯片

如何设置PXE为第一启动项

BxSlider将最后一张幻灯片显示为第一张幻灯片

将excel根据列名称拆分成多个文件

如何根据表格第一列拆分出不同的sheet?