从一个 table 中找到与另一个匹配的人(MS SQL 服务器)
Find matching persons from one table with another (MS SQL Server)
我有两个table:
table“人物”
ID FirstName LastName
----------- ---------- ----------
1 Janez Novak
2 Matija Špacapan
3 Francka Joras
Table“用户列表”
ID FullName
----- --------------------
1 Andrej Novak
2 Novak Peter Janez
3 Jana Novak
4 Andrej Kosir
5 Jan Balon
6 Francka Joras
7 France Joras
因此,查询必须 return 来自两个 table 的那些 ID,table Person 的 FirstName 和 Lastname 存在于 table UserList 中。姓名和姓氏必须完全相同。
table UserList 中的 FullName 可以包含中间名 - 应该被“忽略”。
匹配:Janez Novak = Janez Novak OR Novak Janez OR Janez Peter Novak
不匹配:Janez Novak <> Janeza Novak OR Jjanez Novak
想要的结果:
ID FirstName LastName ID WholeName
---- ---------- --------- ---- -------------------
1 Janez Novak 2 Novak Peter Janez
3 Francka Joras 6 Francka Joras
这是我的查询:
SELECT
A.ID
,A.FirstName
,A.LastName
,B.ID
,B.WholeName
FROM
dbo.UserList B
cross join dbo.Person A
WHERE
(
CHARINDEX('"'+A.FirstName+'"', '"'+Replace(B.WholeName,' ','"')+'"') > 0
AND CHARINDEX('"'+A.LastName+'"', '"'+Replace(B.WholeName,' ','"')+'"') > 0
)
当 table 中的记录不多时,查询工作正常。
但是我的 table 有:“Person” -> 400k 和“UserList” -> 14k 条记录。
我找到解决方案的方法是否可行,或者还有其他更有效的方法吗?
谢谢。
BR
您的架构已损坏 :p
进行匹配有多种启发式方法,但我希望您能够找到反例来打破您尝试的任何方法。例如,彼得·史密斯、皮特·史密斯、彼得·史密森和皮特·史密森这四个人呢?
这是一个 %LIKE%
方法,我预计它会很慢。
SELECT p.ID, p.FirstName, p.LastName, u.ID, u.FullName,
CASE WHEN COUNT(*) OVER (PARTITION BY p.ID) > 1 THEN 0 ELSE 1 END AS MatchIsUnique
FROM Person p
INNER JOIN UserList u
ON u.FullName LIKE p.FirstName + '%'
AND u.LastName LIKE '%' + p.LastName
这是一种基于 space 字符是分隔符的假设的字符串操作方法。
SELECT p.ID, p.FirstName, p.LastName, u.ID, u.FullName,
CASE WHEN COUNT(*) OVER (PARTITION BY p.ID) > 1 THEN 0 ELSE 1 END AS MatchIsUnique
FROM Person p
INNER JOIN UserList u
ON p.FirstName = SUBSTRING(@FullName, 0, CHARINDEX(' ', @Fullname))
AND p.LastName = SUBSTRING(@FullName, LEN(@FullName) - CHARINDEX(' ', REVERSE(@Fullname))+1, CHARINDEX(' ', REVERSE(@Fullname)))
可能也很慢。也许您可以通过添加
来加快速度
SUBSTRING(@FullName, 0, CHARINDEX(' ', @Fullname))
和
SUBSTRING(@FullName, LEN(@FullName) - CHARINDEX(' ', REVERSE(@Fullname))+1, CHARINDEX(' ', REVERSE(@Fullname)))
作为计算列并为其编制索引。
创建表
create table persons (
id int IDENTITY(1,1) PRIMARY KEY,
FirstName nvarchar(32) NOT NULL,
LastName nvarchar(32) NOT NULL
);
create table users (
id int IDENTITY(1,1) PRIMARY KEY,
FullName nvarchar(32) NOT NULL
);
示例数据
INSERT INTO persons (FirstName, LastName)
values
('Janez','Novak'),
('Matija','Špacapan'),
('Francka','Joras');
INSERT INTO users (FullName)
VALUES
('Andrej Novak'),
('Novak Peter Janez'),
('Jana Novak'),
('Andrej Kosir'),
('Jan Balon'),
('Francka Joras'),
('France Joras'),
/* --EDIT: added sample data for wildcard testing-- */
('Franckas Joras'), -- added 's' after firstname
('Francka AJoras'), -- added 'A' before lastname
('Franckas AJoras'), -- both above
('Francka Jr. Joras'), -- added just midname
('Franckas Jr. Joras'); -- added 's' before firstname & added midname as well
查询(匹配名称)
SELECT p.id, p.FirstName, p.LastName, u.id as user_id, u.FullName
FROM persons p, users u
WHERE
-- EDIT
/* changed wildcards (added spaces on both sides)
+ added 2 more conditions without wildcards */
u.FullName LIKE CONCAT(p.FirstName, ' % ', p.LastName)
OR
u.FullName LIKE CONCAT(p.LastName, ' % ', p.FirstName)
OR
u.FullName LIKE CONCAT(p.FirstName, ' ', p.LastName)
OR
u.FullName LIKE CONCAT(p.LastName, ' ', p.FirstName)
输出
编辑: 输出新样本数据(用于通配符测试)
运行 示例 SQL Fiddle
上面的例子 link 是 MySQL 并且代码在 SQL 服务器上工作正常
您可以尝试的一种方法是将全名拆分成行然后进行比较,只选择名字和姓氏匹配的那些:
select Max(m.id) Id, max(m.firstname) FirstName, Max(m.lastname) LastName,
u.id, Max(u.fullname) FullName
from userlist u
cross apply String_Split(fullname,' ')
cross apply (
select *
from person p
where p.firstname = value or p.lastname = value
)m
group by u.id
having Count(*)=2;
输出:
我有两个table:
table“人物”
ID FirstName LastName
----------- ---------- ----------
1 Janez Novak
2 Matija Špacapan
3 Francka Joras
Table“用户列表”
ID FullName
----- --------------------
1 Andrej Novak
2 Novak Peter Janez
3 Jana Novak
4 Andrej Kosir
5 Jan Balon
6 Francka Joras
7 France Joras
因此,查询必须 return 来自两个 table 的那些 ID,table Person 的 FirstName 和 Lastname 存在于 table UserList 中。姓名和姓氏必须完全相同。 table UserList 中的 FullName 可以包含中间名 - 应该被“忽略”。
匹配:Janez Novak = Janez Novak OR Novak Janez OR Janez Peter Novak
不匹配:Janez Novak <> Janeza Novak OR Jjanez Novak
想要的结果:
ID FirstName LastName ID WholeName
---- ---------- --------- ---- -------------------
1 Janez Novak 2 Novak Peter Janez
3 Francka Joras 6 Francka Joras
这是我的查询:
SELECT
A.ID
,A.FirstName
,A.LastName
,B.ID
,B.WholeName
FROM
dbo.UserList B
cross join dbo.Person A
WHERE
(
CHARINDEX('"'+A.FirstName+'"', '"'+Replace(B.WholeName,' ','"')+'"') > 0
AND CHARINDEX('"'+A.LastName+'"', '"'+Replace(B.WholeName,' ','"')+'"') > 0
)
当 table 中的记录不多时,查询工作正常。
但是我的 table 有:“Person” -> 400k 和“UserList” -> 14k 条记录。
我找到解决方案的方法是否可行,或者还有其他更有效的方法吗? 谢谢。
BR
您的架构已损坏 :p
进行匹配有多种启发式方法,但我希望您能够找到反例来打破您尝试的任何方法。例如,彼得·史密斯、皮特·史密斯、彼得·史密森和皮特·史密森这四个人呢?
这是一个 %LIKE%
方法,我预计它会很慢。
SELECT p.ID, p.FirstName, p.LastName, u.ID, u.FullName,
CASE WHEN COUNT(*) OVER (PARTITION BY p.ID) > 1 THEN 0 ELSE 1 END AS MatchIsUnique
FROM Person p
INNER JOIN UserList u
ON u.FullName LIKE p.FirstName + '%'
AND u.LastName LIKE '%' + p.LastName
这是一种基于 space 字符是分隔符的假设的字符串操作方法。
SELECT p.ID, p.FirstName, p.LastName, u.ID, u.FullName,
CASE WHEN COUNT(*) OVER (PARTITION BY p.ID) > 1 THEN 0 ELSE 1 END AS MatchIsUnique
FROM Person p
INNER JOIN UserList u
ON p.FirstName = SUBSTRING(@FullName, 0, CHARINDEX(' ', @Fullname))
AND p.LastName = SUBSTRING(@FullName, LEN(@FullName) - CHARINDEX(' ', REVERSE(@Fullname))+1, CHARINDEX(' ', REVERSE(@Fullname)))
可能也很慢。也许您可以通过添加
来加快速度SUBSTRING(@FullName, 0, CHARINDEX(' ', @Fullname))
和SUBSTRING(@FullName, LEN(@FullName) - CHARINDEX(' ', REVERSE(@Fullname))+1, CHARINDEX(' ', REVERSE(@Fullname)))
作为计算列并为其编制索引。
创建表
create table persons (
id int IDENTITY(1,1) PRIMARY KEY,
FirstName nvarchar(32) NOT NULL,
LastName nvarchar(32) NOT NULL
);
create table users (
id int IDENTITY(1,1) PRIMARY KEY,
FullName nvarchar(32) NOT NULL
);
示例数据
INSERT INTO persons (FirstName, LastName)
values
('Janez','Novak'),
('Matija','Špacapan'),
('Francka','Joras');
INSERT INTO users (FullName)
VALUES
('Andrej Novak'),
('Novak Peter Janez'),
('Jana Novak'),
('Andrej Kosir'),
('Jan Balon'),
('Francka Joras'),
('France Joras'),
/* --EDIT: added sample data for wildcard testing-- */
('Franckas Joras'), -- added 's' after firstname
('Francka AJoras'), -- added 'A' before lastname
('Franckas AJoras'), -- both above
('Francka Jr. Joras'), -- added just midname
('Franckas Jr. Joras'); -- added 's' before firstname & added midname as well
查询(匹配名称)
SELECT p.id, p.FirstName, p.LastName, u.id as user_id, u.FullName
FROM persons p, users u
WHERE
-- EDIT
/* changed wildcards (added spaces on both sides)
+ added 2 more conditions without wildcards */
u.FullName LIKE CONCAT(p.FirstName, ' % ', p.LastName)
OR
u.FullName LIKE CONCAT(p.LastName, ' % ', p.FirstName)
OR
u.FullName LIKE CONCAT(p.FirstName, ' ', p.LastName)
OR
u.FullName LIKE CONCAT(p.LastName, ' ', p.FirstName)
输出
编辑: 输出新样本数据(用于通配符测试)
运行 示例 SQL Fiddle
上面的例子 link 是 MySQL 并且代码在 SQL 服务器上工作正常
您可以尝试的一种方法是将全名拆分成行然后进行比较,只选择名字和姓氏匹配的那些:
select Max(m.id) Id, max(m.firstname) FirstName, Max(m.lastname) LastName,
u.id, Max(u.fullname) FullName
from userlist u
cross apply String_Split(fullname,' ')
cross apply (
select *
from person p
where p.firstname = value or p.lastname = value
)m
group by u.id
having Count(*)=2;
输出: