【问题标题】:Mysql -- Need something like UPDATE [...] ON DUPLICATE KEY [...]Mysql - 需要类似 UPDATE [...] ON DUPLICATE KEY [...]
【发布时间】:2017-10-26 07:56:21
【问题描述】:

我正在验证几个数据库的电子邮件。 我们的验证提供商为我们创建了一个电子邮件列表,其中包含要放置的状态(已检查、未响应等......)

但对于某些地址,我们也进行了修复(例如 foo@gail.com => foo@gmail.com)

我需要输入正确的状态 (20),并且只有时修复电子邮件。我也可能不得不在状态已经正确时修复邮件。

我当前的 PDO 更新请求如下:

$req_fix_mail_and_update_status = "
    UPDATE {$user_table_name}
        SET status = :mail_status1
        , {$mail_column_name} = :mail_fixed1
    WHERE (({$mail_column_name} LIKE :mail_address)  OR  ({$mail_column_name} LIKE :mail_fixed2))
        AND ((status != :mail_status2) OR ({$mail_column_name} NOT LIKE :mail_fixed3))
";

现在,棘手的部分来了:
其中一个数据库存储有关多个网站的数据,并且可能包含邮件重复项。
我对 (fk_site, email) 之类的东西有一个组合的唯一约束
...还有一个极端情况,一些电子邮件同时具有“固定”和“非固定”值条目

例如:

Login   FK_Site  Mail            Status
Alice        01  foo@gmail.com        0
Alice        01  foo@gmal.com         0
Bob          02  foo@gmal.com         0

当然,尝试修复错误的地址会使重复无效,因为 (fk_site, email) 唯一约束在 Alice 上会失败 => 请求失败 (#$%§@&!)。
但是 Bob 仍然需要修复他的邮件,并且线路也需要更新它们的状态!

我正在寻找解决此问题的方法,但无法使用 SQL 找到任何东西。 当我失败时再提出一个(比如说,最多两个)请求可能是可以的,但这似乎是一种狡猾的方式,我不想把任务变成肮脏和不可读的东西,因为这样的边缘 cas

通常,UPDATE [...] with a "ON DUPLICATE KEY <just update status>" 子句是我需要的,但看到它不存在我并不感到惊讶。
我的 worker 是用 PHP 编写的,并且使用 PDO

有什么想法吗?

【问题讨论】:

  • 我会捕获异常,看看它是否在重复键上,然后触发更新查询。
  • @Your Common Sense 我想到了,但是如果我考虑我的示例案例,我不仅要执行新的状态请求,还需要更新 Bob 的错误电子邮件没有尝试更新爱丽丝的。第二次更新将需要修复 only 没有与引发约束违规的其他值等效的行。我尝试使用子选择,但对我来说似乎不可能进行更新。也许带有WHERE NOT EXISTS 子句?我会再挖一遍。
  • 等等,您的源数据没有站点 ID?
  • @Your Common Sense 我当然知道,但我不知道如何在我的请求中利用它。我刚刚测试了以下内容,但由于“无法在 from 子句中指定目标表 p1”而失败。 UPDATE prospect p1 SET firstname='UPDATED', status = 20 WHERE email="<wrong_value>" AND NOT EXISTS ( SELECT id FROM prospect p2 WHERE email = "<right_value>" AND p1.site_id = p2.site_id AND p1.origin = p2.origin ) 请注意,我的唯一约束中有第三列
  • 看起来我找到了一种方法,将WHERE NOT EXISTS 与子选择结合使用。 UPDATE prospect p1 SET email="<fixed_value>", status = 20 WHERE p1.email="<wrong_value>" AND NOT EXISTS ( SELECT id FROM (SELECT * FROM prospect cpy) as p2 WHERE email = "<fixed_value>" AND p1.site_id = p2.site_id AND p1.origin = p2.origin )我对表演有一些疑问,但由于涉及的台词很少,应该问题不大。

标签: php mysql pdo on-duplicate-key


【解决方案1】:

好的,这是我的问题的替代“答案”。 请注意,欢迎使用更简单的解决方案(如果存在...)。

它在技术上解决了我的需求,但非常丑陋,而且我需要 3 个额外的请求而不是 1 个。

  • 一个用于更新有错误邮件的行的邮件和状态,一旦邮件被修复,就不会创建重复
  • 仅更新具有已正确电子邮件的行的状态
  • 仅更新具有由于约束而无法修复的邮件的行的状态

这有点重,但性能在这里不是问题。让我烦恼的是它使代码的可读性降低,需要详细解释才能理解为什么需要 3 个请求。

这是我的 (PDO) 请求,仅更新不会创建重复的行:

UPDATE prospect p1
    SET email = :mail_fixed1
    ,status = :mail_status1
WHERE 
    p1.email = :mail_address
    AND NOT EXISTS (
        SELECT site_id FROM (SELECT site_id, email, origin  FROM  prospect cpy) as p2
        WHERE 
            email = :mail_fixed2
            AND p1.site_id = p2.site_id
            AND p1.origin = p2.origin
        )
  • 唯一性约束在三元组上 (site_id, email, origin)
  • :mail_fixed1:mail_fixed2 等占位符绑定了相同的值。我必须使用不同的原因是因为我的脚本还连接到 SQL-Server 数据库,并且当多次使用占位符时,驱动程序崩溃并出现无法理解的错误。
  • 我的第三个请求,它只更新邮件修复会为其创建副本的行是相同的,除了“不存在”被替换为“存在”(另外,邮件没有被修改,并且状态是固定的,但这些都是细节

中间的第二个请求没有什么特别的,但为了完整起见,我把它放在这里:

UPDATE {$user_table_name}
    SET status = :mail_status1
WHERE ({$mail_column_name} LIKE :mail_address)
    AND ((status IS NULL)  OR  (status != :mail_status2))

我忘了提到另一个小缺点:我的脚本曾经计算更新行的精确数量,但现在它可能会多次计算几行,因为同一行可以被 2 个请求更新。这是一个小细节,但仍然很烦人。

无论如何,我希望这篇文章对其他人有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 2018-05-22
    • 2011-08-09
    • 2014-09-29
    相关资源
    最近更新 更多