MySQL:如果未达到第二轮标准,则删除匹配行的JOIN

时间:2015-06-09 21:22:22

标签: mysql join left-join match

简明版

我尝试使用我的现有数据库加入一个没有唯一标识符的新列表 - 但是我试图找到一种方法来在一个查询中找到一个比按名字/姓氏匹配但不是特定于所有可用字段(名字/中间名/姓氏/地址/电话)。

所以我的想法是仅匹配姓/名,然后尝试为每个可能的匹配字段分配点,以查看是否有人匹配了零点'因此从它们中删除了名字/姓氏匹配。这就是我想出的:

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));

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

有没有办法用我的环形交叉点系统做我正在寻找的东西?我发现在尝试识别要选择的副本时,它对我有很大的帮助,因此我尝试将其扩展到初始匹配。或者我应该做些不同的事情?

具体版本

这是一个迂回的想法 - 所以如果有人有更直接的东西,我绝对愿意完全保释,并尝试别的东西。但基本上我有一个93k人表(来自数据库),我与92k人表(从新列表)匹配。我希望它们中的许多是相同的,但肯定不是全部 - 我试图避免创建重复项。不幸的是,没有可以匹配的唯一标识符,所以我通常会根据名字,中间名,姓氏,地址和/或电话号码的某些变体进行匹配。

两个表(列表和数据库)的模式与您在上面看到的字段(名字,中间名,姓氏,地址,电话)非常相似 - 唯一的区别是数据库表还有一个我将用于在此匹配后上传回数据库的唯一数字ID。不幸的是,列表没有这样的ID。带有ID的记录将匹配并加载到旧记录的顶部,任何没有该ID的记录都将作为新记录加载。

我试图避免这个问题的是创建一堆不同的表和查询,这些表和查询以一个非常具体的JOIN语句开头,然后最终归结为名字和姓氏 - 从那以后可能有些人应该匹配,但是自上一个列表以来已经移动和/或获得了新的电话号码。

我可以写一个非常简单的查询作为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;

我当然有信心新列表中的那些人与我的数据库中的现有人匹配,但它只返回了极少数人,然后我就有了返回并放宽标准(例如,删除中间名称限制等)并不断创建表格,然后将它们全部重新合并在一起,以及所有那些根本不匹配的表格,我假设将是新人。

但是有没有办法只使用名字/姓氏匹配来编写查询,然后评估其他标准并从没有积分的人那里擦除匹配。 (下面)?以下是我尝试为每场比赛分配[任意]分数的方法:

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));

LEFT语句中的RIGHTIF公式只是试图控制发送的非标准化数据。我也会用WHERE语句做一些事情,但我仍然需要返回NULL个值,所以我知道谁匹配了谁以及谁没有。所以我最终试图在IF中使用LEFT JOIN语句来说明如果Points单元格等于零,那么JOIN语句将变得非常具体,我认为有希望仍然会返回该行,但即使它们的名字和名字都没有,也不会与数据库匹配。

查询不会产生任何错误,但不幸的是,我仍然让人们回到Points列但是与数据库匹配的人,因为他们的名字和姓氏相匹配(是我希望IF / Points的东西会停止的。)

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

谢谢,希望这有点意义!

1 个答案:

答案 0 :(得分:1)

您的第一个查询:

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;

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

编辑:

你的评论明确表达了。你不仅需要名字和姓氏,还需要其他东西。

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;