2

我在使用 T-SQL 时遇到了困难,我想知道是否有人能给我指出正确的方向。我有以下名为@input 的变量

    DECLARE @input nvarchar(100);
    SET @input= '27364 - John Smith';
   -- SET @input= '27364 - John Andrew Smith';

如果字符串包含中间名,我需要将此字符串拆分为 3 部分(ID、名字和姓氏)或 4 部分。出于安全原因,我不能使用函数。

我的方法是使用 Substring 和 Charindex。

SET @Id = SUBSTRING(@input, 1, CASE CHARINDEX('-', @input)
                    WHEN 0
                        THEN LEN(@input)
                    ELSE 
                        CHARINDEX('-', @input) - 2
                    END);
        SET @FirstName = SUBSTRING(@input, CASE CHARINDEX(' ', @input)
                    WHEN 0
                        THEN LEN(@input) + 1
                    ELSE 
                        CHARINDEX(' ', @input) + 1
                    END, 1000);
        SET @LastName = SUBSTRING(@input, CASE CHARINDEX(' ', @input)
                    WHEN 0
                        THEN LEN(@input) + 1
                    ELSE 
                        CHARINDEX('0', @input) + 1
                    END, 1000);
Select @PartyCode,@FirstName,@LastName 

我被卡住了,因为我不知道如何继续,而且如果存在 Middlename,代码必须足够聪明才能添加第四个拆分。

有什么想法吗?

提前致谢

4

2 回答 2

4

希望这是规范化项目的一部分。这些数据打破了 1NF,我们真的应该避免这种情况......

像这样试试

优点

  • 类型安全值
  • 即席 SQL
  • 基于集合

如果你愿意,你可以使用 aCASE WHEN检查最后一部分是否是NULL,在这种情况下将 Part2 放入 Part3 ......

DECLARE @input table(teststring nvarchar(100));
INSERT INTO @input VALUES
(N'27364 - John Smith'),(N'27364 - John Andrew Smith');

WITH Splitted AS
(
    SELECT CAST(N'<x>' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(teststring,N' - ',N' '),N'&',N'&amp;'),N'<',N'&lt;'),N'>',N'&gt;'),N' ',N'</x><x>') + N'</x>' AS XML) testXML
    FROM @input
)
SELECT testXML.value('/x[1]','int') AS Number
      ,testXML.value('/x[2]','nvarchar(max)') AS Part1 
      ,testXML.value('/x[3]','nvarchar(max)') AS Part2 
      ,testXML.value('/x[4]','nvarchar(max)') AS Part3 
FROM Splitted

结果

Number  Part1   Part2   Part3
27364   John    Smith   NULL
27364   John    Andrew  Smith
于 2016-07-07T12:54:30.573 回答
0

SQL Server 2016 有一个名为 STRING_SPLIT() 的新内置函数

假设创建内置函数,但不允许使用 CLR 函数:

CREATE FUNCTION dbo.WORD_SPLIT
(   
    @String AS nvarchar(4000)
)
RETURNS TABLE 
AS
RETURN 
(
    WITH Spaces AS
    (
        SELECT Spaced.[value], ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY (SELECT 1)) AS ordinal
        FROM STRING_SPLIT(@String, ' ') AS Spaced
    )
    , Tabs AS
    (
        SELECT Tabbed.[value], ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY s.ordinal, (SELECT 1)) AS ordinal
        FROM Spaces AS s
            CROSS APPLY STRING_SPLIT(s.[value], '   ') AS Tabbed
    )
    , NewLines1 AS
    (
        SELECT NewLined1.[value], ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY t.ordinal, (SELECT 1)) AS ordinal
        FROM Tabs AS t
            CROSS APPLY STRING_SPLIT(t.[value], CHAR(13)) AS NewLined1
    )
    , NewLines2 AS
    (
        SELECT NewLined2.[value], ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY nl1.ordinal, (SELECT 1)) AS ordinal
        FROM NewLines1 AS nl1
            CROSS APPLY STRING_SPLIT(nl1.[value], CHAR(10)) AS NewLined2
    )
    SELECT LTRIM(RTRIM(nl2.[value])) AS [value], ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY nl2.ordinal, (SELECT 1)) AS ordinal
    FROM NewLines2 AS nl2
    WHERE LTRIM(RTRIM(nl2.[value])) <> ''
)
GO

用法:

-- Not Normailized
SELECT i.*, split.[value], split.[ordinal]
FROM @input AS i
    CROSS APPLY dbo.WORD_SPLIT(i.teststring) AS split

-- Normalized
;WITH Splitted AS
(
    SELECT split.[value], split.[ordinal]
    FROM @input AS i
        CROSS APPLY dbo.WORD_SPLIT(i.teststring) AS split
)
SELECT *
FROM (SELECT [value], 'part' + CONVERT(nvarchar(20), [ordinal]) AS [parts] FROM Splitted) AS s
    PIVOT (MAX([value]) FOR [parts] IN ([part1], [part2], [part3], [part4])

或者假设,根据安全性,您不允许进行架构更改:

WITH Splitting AS
(
    SELECT teststring AS [value]
    FROM @input
)
WITH Spaces AS
(
    SELECT Spaced.[value], ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY (SELECT 1)) AS ordinal
    FROM Splitting AS sp
        CROSS APPLY STRING_SPLIT(sp.[value], ' ') AS Spaced
)
, Tabs AS
(
    SELECT Tabbed.[value], ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY s.ordinal, (SELECT 1)) AS ordinal
    FROM Spaces AS s
        CROSS APPLY STRING_SPLIT(s.[value], '   ') AS Tabbed
)
, NewLines1 AS
(
    SELECT NewLined1.[value], ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY t.ordinal, (SELECT 1)) AS ordinal
    FROM Tabs AS t
        CROSS APPLY STRING_SPLIT(t.[value], CHAR(13)) AS NewLined1
)
, NewLines2 AS
(
    SELECT NewLined2.[value], ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY nl1.ordinal, (SELECT 1)) AS ordinal
    FROM NewLines1 AS nl1
        CROSS APPLY STRING_SPLIT(nl1.[value], CHAR(10)) AS NewLined2
)
, Splitted AS
(
    SELECT LTRIM(RTRIM(nl2.[value])) AS [teststring], ROW_NUMBER() OVER (PARTITION BY 1 ORDER BY nl2.ordinal, (SELECT 1)) AS ordinal
    FROM NewLines2 AS nl2
    WHERE LTRIM(RTRIM(nl2.[value])) <> ''
)
SELECT *
FROM (SELECT [value], 'part' + CONVERT(nvarchar(20), [ordinal]) AS [parts] FROM Splitted) AS s
    PIVOT (MAX([value]) FOR [parts] IN ([part1], [part2], [part3], [part4])

希望有帮助!

于 2017-08-22T15:36:26.087 回答