SQL 服务器:使用一个现有列的拆分内容更新两列
SQL Server : update two columns with the split content of one existing column
我有一个 SQL 服务器 table 具有这些列:
ID, Name, FirstName, LastName
所有名称现在都存储在 Name
列中,我想 运行 更新以填充 FirstName
和 LastName
列。
当前 table 的样本如下所示:
1 | John Doe | NULL | NULL
更新后我希望这样:
1 | John Doe | John | Doe
为简单起见,可以做出以下假设:
Name
列始终包含由 space 分隔的名字和姓氏。
- 名字和姓氏始终是一个单词的名字,因此没有额外的 space。
我已经尝试了以下(首先作为 SELECT,这样我可以在 运行 更新之前看到输出):
SELECT
(SELECT TOP 2 value FROM STRING_SPLIT([NAME], ' ')) AS FirstName,
(SELECT TOP 1 value FROM STRING_SPLIT([NAME], ' ')) AS LastName
FROM
UserTable
WHERE
LastName IS NULL
AND [NAME] IS NOT NULL
但是这不起作用,因为第一列将 return 前两个顶部条目。但我想获得第二个条目,而不是前两个。第二列(前 1 列)就可以了。
我看到 STRING_SPLIT
提供了第三个参数 -> 序数。这将允许我向子查询添加 WHERE 子句。然而,这仅在 Azure 中可用,但我目前正在 运行ning SQL Server 2016 (v13)(没有 Azure)。
在 SQL Server 2016 中实现我的 UPDATE
的最佳方法是什么?
鉴于您指定的具体限制条件:
您可以使用 SUBSTRING
和 CHARINDEX
的组合
- 注意使用
CROSS APPLY (VALUES
以允许重用 CHARINDEX
计算
- 注意使用
NULLIF
以防止在没有space 的情况下出错
SELECT
SUBSTRING(t.NAME, 1, v.space - 1) AS FirstName,
SUBSTRING(t.NAME, v.space + 1, LEN(t.NAME)) AS LastName
FROM UserTable t
CROSS APPLY (VALUES(
NULLIF(CHARINDEX(' ', t.NAME), 0)
)) v(space)
WHERE t.LastName IS NULL
AND t.[NAME] IS NOT NULL;
请注意,这绝不是拆分名称的通用解决方案。
名字可以多种多样,可能在名字之前有姓氏,可能没有姓氏,可能有多个名字或姓氏等。不要做假设。
这是另一种通过内置 PARSENAME()
函数使用标记化的方法。
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, Name VARCHAR(100), FirstName VARCHAR(50), LastName VARCHAR(50));
INSERT INTO @tbl (Name, FirstName, LastName)
VALUES
('John Doe', NULL, NULL),
('Anna Mac', NULL, NULL);
-- DDL and sample data population, end
SELECT ID
, FirstName = PARSENAME(tokens, 2)
, LastName = PARSENAME(tokens, 1)
FROM @tbl
CROSS APPLY (SELECT REPLACE(Name, SPACE(1),'.')) AS t(tokens);
输出
+----+-----------+----------+
| ID | FirstName | LastName |
+----+-----------+----------+
| 1 | John | Doe |
| 2 | Anna | Mac |
+----+-----------+----------+
我有一个 SQL 服务器 table 具有这些列:
ID, Name, FirstName, LastName
所有名称现在都存储在 Name
列中,我想 运行 更新以填充 FirstName
和 LastName
列。
当前 table 的样本如下所示:
1 | John Doe | NULL | NULL
更新后我希望这样:
1 | John Doe | John | Doe
为简单起见,可以做出以下假设:
Name
列始终包含由 space 分隔的名字和姓氏。- 名字和姓氏始终是一个单词的名字,因此没有额外的 space。
我已经尝试了以下(首先作为 SELECT,这样我可以在 运行 更新之前看到输出):
SELECT
(SELECT TOP 2 value FROM STRING_SPLIT([NAME], ' ')) AS FirstName,
(SELECT TOP 1 value FROM STRING_SPLIT([NAME], ' ')) AS LastName
FROM
UserTable
WHERE
LastName IS NULL
AND [NAME] IS NOT NULL
但是这不起作用,因为第一列将 return 前两个顶部条目。但我想获得第二个条目,而不是前两个。第二列(前 1 列)就可以了。
我看到 STRING_SPLIT
提供了第三个参数 -> 序数。这将允许我向子查询添加 WHERE 子句。然而,这仅在 Azure 中可用,但我目前正在 运行ning SQL Server 2016 (v13)(没有 Azure)。
在 SQL Server 2016 中实现我的 UPDATE
的最佳方法是什么?
鉴于您指定的具体限制条件:
您可以使用 SUBSTRING
和 CHARINDEX
- 注意使用
CROSS APPLY (VALUES
以允许重用CHARINDEX
计算 - 注意使用
NULLIF
以防止在没有space 的情况下出错
SELECT
SUBSTRING(t.NAME, 1, v.space - 1) AS FirstName,
SUBSTRING(t.NAME, v.space + 1, LEN(t.NAME)) AS LastName
FROM UserTable t
CROSS APPLY (VALUES(
NULLIF(CHARINDEX(' ', t.NAME), 0)
)) v(space)
WHERE t.LastName IS NULL
AND t.[NAME] IS NOT NULL;
请注意,这绝不是拆分名称的通用解决方案。
名字可以多种多样,可能在名字之前有姓氏,可能没有姓氏,可能有多个名字或姓氏等。不要做假设。
这是另一种通过内置 PARSENAME()
函数使用标记化的方法。
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, Name VARCHAR(100), FirstName VARCHAR(50), LastName VARCHAR(50));
INSERT INTO @tbl (Name, FirstName, LastName)
VALUES
('John Doe', NULL, NULL),
('Anna Mac', NULL, NULL);
-- DDL and sample data population, end
SELECT ID
, FirstName = PARSENAME(tokens, 2)
, LastName = PARSENAME(tokens, 1)
FROM @tbl
CROSS APPLY (SELECT REPLACE(Name, SPACE(1),'.')) AS t(tokens);
输出
+----+-----------+----------+
| ID | FirstName | LastName |
+----+-----------+----------+
| 1 | John | Doe |
| 2 | Anna | Mac |
+----+-----------+----------+