【问题标题】:Split Name Column which can contain Multiple Names and no delimiter into Person 1 and Person 2将可以包含多个名称且没有分隔符的名称列拆分为人员 1 和人员 2
【发布时间】:2020-04-21 18:43:12
【问题描述】:

如何将可以包含 2 个名称的字符串拆分为 Person1 和 Person2 ?名称之间没有分隔符,每行并不总是有第二个人,第一人称或第二人称也不一定有中间名首字母/名字,只有有时第二个名字会用“AND”分​​隔 名称示例如下

  • 简·米德尔顿约翰·米德尔顿
  • 苏弗拉卡罗鲍勃弗拉卡罗
  • 托尼法语
  • 约翰·爱德华多·奥乔亚和简·阿德里安娜·奥乔亚
  • 托尼·约翰·卡彭特托尼亚·卡彭特

所需的输出设计

  • 个人 1 名
  • 人物 1 中间名
  • 第 1 个人姓氏
  • 人物 2 名
  • 人物 2 中间名
  • 个人 2 姓

【问题讨论】:

  • 您运行的是什么版本的 SQL Server? (SELECT @@VERSION)
  • Microsoft SQL Server 2012 SP3
  • 不幸的是,这是非常常见的情况,这是设计不佳的直接结果。您最好的选择是聘请专业的数据架构师并重新设计您的数据库!如果这是一张表的设计方式,那么我很确定您的整个数据库应该重新设计,也许整个系统都应该重新设计。
  • 澄清一下,我没有否决你的问题,我认为没有不好的问题,只有不好的提问方法和不好的回答。第一种情况是 OP 可以修复,第二种情况是获得免费支持的一部分。无论如何,我一般都反对在论坛中投反对票的能力
  • 只是重复Ronen所说的,您需要提供如何拆分字符串的确切规则,然后我们才能协助构建查询。我怀疑您将无法创建 100% 准确的规则。例如,您的最后一个示例有 5 个单词,我们将使用什么规则来知道前 3 个适用于第 1 个人,最后 2 个适用于第 2 个人?反过来也一样容易。

标签: sql sql-server tsql parsing split


【解决方案1】:

创建一个函数,用于从某个表列中拆分有问题的字符串值:

CREATE FUNCTION [dbo].[Fn_Splittemp]
        (
          @text VARCHAR(8000)
        , @delimiter VARCHAR(20) = ' '
        )
        RETURNS @String TABLE
            (
                  Position INT IDENTITY PRIMARY KEY
                , StringValue VARCHAR(8000)
            )
        AS
        BEGIN
            DECLARE @index INT
            SET @index = -1
            WHILE (LEN(@text) >0)
                    BEGIN
                        SET @index = CHARINDEX(@delimiter, @text)
                            IF (@index = 0) AND (LEN(@text) > 0)
                                BEGIN
                                    INSERT INTO @string VALUES (@text)
                                    BREAK
                                END
                            IF (@index > 1)
                                BEGIN
                                    INSERT INTO @String VALUES (LEFT(@text, (@index-1)))
                                    SET @text = RIGHT(@text, (LEN(@text)-@index))
                                END
                            ELSE
                                    SET @text = RIGHT(@text, (LEN(@text)-@index))
                                END
                        RETURN
                    END
    GO

拆分、清理和分配给各自的 name_columns:

CREATE TABLE #t0 (rid INT IDENTITY, rawnames VARCHAR(8000));
GO

INSERT INTO #t0 VALUES ('JANE MIDDLETON John MIDDLETON'),
('SUE FRACARO BOB FRACARO'),
('TONY FRENCH'),
('JOHN EDUARDO OCHOA AND JANE ADRIANA OCHOA'),
('TONY JOHN CARPENTER TONYA CARPENTER');
GO

SELECT n.rid, n.rawnames, fn.StringValue AS Names, 
COUNT(*) OVER(PARTITION BY rawnames) AS wordcount,
ROW_NUMBER() OVER(PARTITION BY fn.stringvalue,rawnames ORDER BY fn.stringvalue) AS LastNameids,
fn.Position
INTO #t1
FROM #t0 n
cross apply dbo.Fn_Splittemp(n.rawnames, ' ') AS fn
GO

SELECT rid, rawnames, Position AS Pid, 
PersonName, LastName INTO #t2
FROM
(SELECT t.rid, t.rawnames, t.names AS Lastname, LTRIM(RTRIM(REPLACE(f.StringValue,'and',''))) AS PersonName, f.Position
FROM
    (SELECT replace(sqa.rawnames,sqa.Names,sqa.Names+',') AS delimstr , sqa.* 
        FROM #t1 sqa
        WHERE wordcount<=3 AND position = (SELECT MAX(position) from #t1 crq where crq.rid = sqa.rid)
    )t
cross apply dbo.Fn_Splittemp(delimstr,',') f
UNION ALL
SELECT b.rid, b.rawnames, b.Names AS Lastname, LTRIM(RTRIM(REPLACE(f.StringValue,'and',''))) AS PersonName, f.Position
FROM 
(SELECT replace(rawnames,names,names+',') AS delimstr, *
FROM #t1 
WHERE wordcount>3 AND LastNameids>1)b
cross apply dbo.Fn_Splittemp(delimstr,',') f
)sqt
GO

SELECT * INTO #t3 FROM #t2 cross apply dbo.Fn_Splittemp(personname, ' ');
GO

SELECT t.rawnames, fina.Firstname, fina.MiddleName, fina.LastName
FROM #t0 t
JOIN (
SELECT rid, pid, [1] AS Firstname, NULL AS MiddleName, [2] AS LastName 
FROM
(SELECT * FROM (SELECT rid, pid, position, stringvalue, 
COUNT(*) OVER(PARTITION BY rid, pid) AS cnt FROM #t3)a
WHERE a.cnt <=2)apiv
PIVOT
(MAX(stringvalue)
    FOR position IN ([1],[2])
    )piva
UNION ALL
SELECT rid, pid, [1] AS Firstname, [2] AS MiddleName, [3] AS LastName
FROM
(SELECT * FROM (SELECT rid, pid, position, stringvalue, 
COUNT(*) OVER(PARTITION BY rid, pid) AS cnt FROM #t3)a
WHERE a.cnt >2)apiv
PIVOT
(MAX(stringvalue)
    FOR position IN ([1],[2],[3])
    )piva
)fina
ON fina.rid = t.rid;

【讨论】:

  • 旁白:有很多关于分割字符串的更好方法的参考资料,例如Tally OH! An Improved SQL 8K “CSV Splitter” Function.
  • KGRed 提供的解决方案似乎适用于例外情况,如果名称具有中间名首字母或中间名,例如“John A Doe”,所以我不确定为什么它在有中间名首字母。
  • 是的,这是一个错误。对于 wordcount WHERE wordcount<=3 AND position = (SELECT MAX(position) from #t1 crq where crq.rid = sqa.rid)
  • 谢谢!您的解决方案适用于我的大部分数据(88K 记录),并进行了一些调整以适合我的数据。我将手动解析出它不起作用的记录。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-25
  • 1970-01-01
  • 2021-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多