MySQL:如果不满足第二轮条件,则删除匹配行的 JOIN

MySQL: Remove JOIN for Matched Row if 2nd Round of Criteria Not Met

精简版

我正在尝试使用没有唯一标识符的现有数据库加入一个新列表 -- 但我正在尝试找出一种在一个查询中执行此操作的方法,该查询比第一个匹配更具体 name/last name 但不如所有可用字段具体(第一个 name/middle name/last name/address/phone)。

所以我的想法是只匹配 first/last 名称,然后尝试为每个可能的匹配字段分配点数,看看是否有匹配的人有 'zero points',因此有第一个 name/last 名称匹配从他们身上剥离。这是我想出的:

SELECT *, 
@MidMatch := IF(LEFT(l.middle,1)=LEFT(d.middle,1),"TRUE","FALSE") MidMatch, 
@AddressMatch := IF(left(l.address,5)=left(d.address,5),"TRUE","FALSE") AddressMatch, 
@PhoneMatch := IF(right(l.phone,4)=right(d.phone,4),"TRUE","FALSE") PhoneMatch,  
@Points := IF(@MidMatch = "TRUE",4,0) + IF(@AddressMatch = "TRUE",3,0) + IF(@PhoneMatch = "TRUE",1,0) Points
    FROM list l
    LEFT JOIN database d on IF(@Points <> 0,(l.first = d.first AND l.last = d.last),(l.first = d.first AND l.last = d.last AND l.address = d.vaddress));

查询运行正常,但它仍然匹配 first/last 姓名相同的人,即使他们的分数为零(并且如果他们的地址不匹配)。

有没有办法用这个环岛积分系统来实现我正在寻找的东西?我发现它在尝试确定要选择的重复项时对我有很大帮助,因此我正在尝试将其扩展到初始匹配项。或者我应该做些不同的事情吗?


具体版本

这是一个迂回的想法——所以如果有人有更直接的东西,我肯定愿意完全放弃这个并尝试其他东西。但基本上我有一个 93k 的人 table(来自数据库),我正在匹配一个 92k 的人 table(来自一个新列表)。我希望它们中的许多是相同的,但肯定不是全部——我正在努力避免创建重复项。不幸的是,没有可以匹配的唯一标识符,所以我通常坚持基于名字、中间名、姓氏、地址、and/or phone 号码的一些变体进行匹配。

两个 table 的架构(列表和数据库)与您在上面看到的字段(名字、中间名、姓氏、地址,phone)完全相同 --唯一的区别是数据库 table 也有一个唯一的数字 ID,我将在这场比赛后用它上传回数据库。不幸的是,列表 table 没有这样的 ID。具有 ID 的记录将被匹配并加载到旧记录之上,没有该 ID 的任何记录将作为新记录加载。

这个问题我想避免的是创建一堆不同的 tables 和查询,这些查询以一个非常具体的 JOIN 语句开始,然后最终下降到 first and姓氏——因为可能有些人应该匹配但已经搬家 and/or 自上次列表以来获得了一个新的 phone 号码。

我可以将一个非常简单的查询写成 JOIN 并重复多次,每次都取出另一个限定符:

SELECT * 
FROM list l
JOIN database d
ON d.first = l.first AND d.last = l.last AND d.middle = l.middle AND d.address = l.address AND d.phone = l.phone;

而且我当然相信新列表中的那些人与我数据库中的现有人员相匹配,但只有 return 极少数人,那么我会有返回并放宽标准(例如,放弃中间名限制等)并不断创建 tables 然后在最后将它们与所有完全不匹配的那些合并在一起,这我想会是新人。

但是有没有一种方法可以只使用 first/last 名称匹配来编写查询,然后评估其他条件并从 'points' 为零的人中删除匹配(如下)?这是我尝试为每场比赛分配 [任意] 点的方法:

SELECT *, 
@MidMatch := IF(LEFT(l.middle,1)=LEFT(d.middle,1),"TRUE","FALSE") MidMatch, 
@AddressMatch := IF(left(l.address,5)=left(d.address,5),"TRUE","FALSE") AddressMatch, 
@PhoneMatch := IF(right(l.phone,4)=right(d.phone,4),"TRUE","FALSE") PhoneMatch,  
@Points := IF(@MidMatch = "TRUE",4,0) + IF(@AddressMatch = "TRUE",3,0) + IF(@PhoneMatch = "TRUE",1,0) Points
    FROM list l
    LEFT JOIN database d on IF(@Points <> 0,(l.first = d.first AND l.last = d.last),(l.first = d.first AND l.last = d.last AND l.address = d.vaddress));

LEFTRIGHT 语句中的 IF 公式只是试图控制发送的非标准化数据。我也会用 WHERE 语句做一些事情,但我仍然需要 NULL 值到 return 所以我知道谁匹配,谁不匹配。所以我最终尝试在 LEFT JOIN 中使用 IF 语句来表示如果 Points 单元格等于零,那么 JOIN 语句将变得非常具体并且我认为希望仍然 return 该行,但即使他们的名字和姓氏匹配,它也不会与数据库匹配。

查询没有产生任何错误,但不幸的是,我仍然会找回在 Points 列中有零但与数据库匹配的人,因为他们的名字和姓氏匹配(这就是我希望 IF/Points 事情能停止)。

这可能是避免不良匹配的一种方法,还是我走错了路?如果这不是正确的方法,是否有任何其他方法可以编写一个查询,该查询将 return 一个完整的 LEFT JOIN 以及不匹配但具有它的 NULL比 first/last 名称更具体,但比每次基于新的 table 进行一百万次查询的工作更少?

谢谢,希望这能说得通!

您的第一个查询:

SELECT *, 
       @MidMatch := IF(LEFT(l.middle,1)=LEFT(d.middle,1),"TRUE","FALSE") MidMatch, 
       @AddressMatch := IF(left(l.address,5)=left(d.address,5),"TRUE","FALSE") AddressMatch, 
       @PhoneMatch := IF(right(l.phone,4)=right(d.phone,4),"TRUE","FALSE") PhoneMatch,  
       @Points := IF(@MidMatch = "TRUE",4,0) + IF(@AddressMatch = "TRUE",3,0) + IF(@PhoneMatch = "TRUE",1,0) Points
    FROM list l LEFT JOIN
         database d
         on IF(@Points <> 0,(l.first = d.first AND l.last = d.last),(l.first = d.first AND l.last = d.last AND l.address = d.vaddress));

这在变量方面犯了 严重的 错误。最简单的是SELECT——SELECT不保证表达式的计算顺序,所以可以按任意顺序计算。如果先计算@Points,逻辑是错误的。在不同的子句中引用变量会使这个问题更加复杂。 SQL 语句是描述结果集的 逻辑 语句,而不是关于查询方式的编程语句 运行.

让我假设您对数据库中的每一行都有一个唯一标识符(只是为了标识该行)。然后你可以通过使用相关子查询来获得匹配:

select l.*,
       (select d.databaseid
        from database d
        where l.first = d.first and l.last = d.last
        order by (4 * (LEFT(l.middle, 1) = LEFT(d.middle, 1) ) +
                  3 * (left(l.address, 5) = left(d.address, 5)) +
                  1 * (right(l.phone, 4) = right(d.phone, 4))
                 )
        limit 1
       ) as did
from list l;

如果需要,您可以重新加入数据库 table 以获取更多信息。

编辑:

你的评论说得很清楚了。您不仅需要名字和姓氏,还需要其他内容。

select l.*,
       (select d.databaseid
        from database d
        where l.first = d.first and l.last = d.last and
              (LEFT(l.middle, 1) = LEFT(d.middle, 1) or
               left(l.address, 5) = left(d.address, 5) or
               right(l.phone, 4) = right(d.phone, 4)
              )                     
        order by (4 * (LEFT(l.middle, 1) = LEFT(d.middle, 1) ) +
                  3 * (left(l.address, 5) = left(d.address, 5)) +
                  1 * (right(l.phone, 4) = right(d.phone, 4))
                 )
        limit 1
       ) as did
from list l;