SQL:如果 *condition* 在另一行上,则更新一行

Posted

技术标签:

【中文标题】SQL:如果 *condition* 在另一行上,则更新一行【英文标题】:SQL: update a row if *condition* on another row 【发布时间】:2020-08-03 04:44:14 【问题描述】:

我的 SQL 有问题。

当且仅当另一行上的值与条件匹配时,我想更新给定行上的值;然后更新第二行的另一个值。

好吧,这样解释不清楚,所以代码如下(我要使用mysqli绑定参数):

--mariaDB:

UPDATE `accountlist` JOIN `data` ON `accountlist`.`id`=`data`.`id`
    SET `upvotes`= `upvotes` + (`user`= ?),
        `allow` = (CASE WHEN `accountlist`.`id` = ?
                   THEN ?
                   ELSE `allow`
                   END)
WHERE (SELECT `allow` FROM `data` WHERE `id` = ?) < ?;

--mysql:

UPDATE `accountlist` JOIN `data` ON `accountlist`.`id`=`data`.`id`
    SET `upvotes`= `upvotes` + (`user`= ?),
        `allow` = (CASE WHEN `accountlist`.`id` = ?
                   THEN ?
                   ELSE `allow`
                   END)
WHERE `data`.`id` = ? AND `allow` < ?;

--params sample: "admin", 2, "2020-04-20", 2, "2020-04-20"
--               (same value here means always same value)

我必须同时保留 MySQL 和非 MySQL 版本,因为我在 localhost 和我的主机上有不同的数据库,并且由于某种原因第一个版本不适用于 MySQL。

data 是另一个与accountlist 一对一关系的表(它们始终具有相同的行数和相同的ids)

无论如何,我将调用 user=? 所在的行 row 1accountlist.id=? 所在的行 row 2 以简化所有内容。

我想做的是:

    如果第2行上的allow小于?,则更新第1行上的user 更新第2行上的allow(如果第2行上的allow小于?

第二点的条件并不重要,因为它会自我更新,但这是我能够做到这一点的唯一方法。

我的问题:

第一段代码(非 MySQL)更新 第 1 行上的 upvotes,但永远不会更改 第 2 行上的 allow。 第二段代码 (MySQL) 更新 第 2 行 上的 arrow,但永远不会更改 第 1 行 上的 user

您是否有任何解决方案,可能意味着唯一的 MySQL + 非 MySQL 版本?



更新:

这里是一个例子:

     accountlist                       data
   |------------|---------------|    |------------|--------------|----------|
   | id         | user          |    | id         | allow        | upvotes  |
   |------------|---------------|    |------------|--------------|----------|
A: | 1          | admin         |    | 1          | 2020-04-18   | 2        |
B: | 2          | foo           |    | 2          | 2020-04-20   | 0        |
C: | 3          | bar           |    | 3          | 2020-04-22   | 1        |
   |------------|---------------|    |------------|--------------|----------|

参数:“管理员”、2、“2020-04-20”、2、“2020-04-20” B 行上的allow低于2020-04-20

什么都没有发生。

参数:“管理员”、2、“2020-04-22”、2、“2020-04-22” B 行 上的allow 低于2020-04-20

A 行 上的upvotes 增加了 (2->3) B 行 上的allow 已更新 (2020-04-20->2020-04-22)

参数:“bar”、1、“2020-04-19”、1、“2020-04-19” A 行 上的allow 低于2020-04-19

C 行 上的upvotes 增加了 (1->2) A 行 上的allow 已更新 (2020-04-18->2020-04-19)

更新 2:

我想做什么: 如果 user 1 想要给 user 2 投票(每个人每天最多可以给其他人投票一次),当它点击一个按钮时,allowed(在他的行)是与后天相比:

如果allowed 等于后天,则表示用户1已经为某人投票(他可能是用户2用户 3 或其他人),所以没有任何变化 如果allowed 低于次日,这意味着用户1 可以投票给某人,所以upvote用户2 的行增加,用户 1 的行上的allow 更新为后一天

不用担心“如果 user 1user 2 实际不存在怎么办?”或“如果 user 1 怎么办? > 试图给自己投票?* 因为我在我的 php 代码中检查了它。

【问题讨论】:

这可能是可行的,但你的问题目前还不清楚。请以表格文本的形式提供示例数据和预期结果,以阐明您的实际需求(通常,5 到 10 行就足够了)。 如何定义“第 1 行”?基于id?基于某种顺序的“下一个”,如果是,是什么顺序? 为什么不能对两个数据库都使用第二个版本? @GMB 我更新的问题够了吗? 你为什么不做类似“检查用户是否被允许投票、增加投票、更新日期”之类的事情?无论如何,您的 mariadb 代码应该可以工作。对于所有边缘情况(例如,它不检查第 1 行是否存在),它不快速或不美观,但它不应该有您描述的问题 - 除非data 中的第 1 行或第 2 行没有匹配条目。请检查并澄清data 是否相关,您永远不会提及它。让这个 mariadb 查询在 mysql 中工作是微不足道的(“我这样做是因为我在 MySQL 中遇到错误”比“我读到它”更好地解释了你的原因),但你应该先澄清你的要求。 【参考方案1】:

让我们来看看。首先,对每一列进行限定,以帮助查看哪一列在哪个表中:

UPDATE a JOIN d ON a.`id` = d.`id`
    SET d.`upvotes`= d.`upvotes` + (a.`user`= ?),
        d.`allow` = (CASE WHEN a.`id` = ?
                   THEN ?
                   ELSE d.`allow`
                   END)
WHERE d.`id` = ? AND d.`allow` < ?;

哦,这两张桌子在id 上是 1:1 的。所以让我们假设它们是同一张桌子。 (你可能应该这样做。1:1 通常不是一个好的架构设计。)

UPDATE ad
    SET `upvotes`= `upvotes` + (`user`= ?),
        `allow` = (CASE WHEN `id` = ?
                   THEN ?
                   ELSE `allow`
                   END)
WHERE `id` = ? AND `allow` < ?;

现在很明显CASE 被浪费了。所以查询简化为

UPDATE ad
    SET `upvotes`= `upvotes` + (`user`= ?),
        `allow` = ?
WHERE `id` = ? AND `allow` < ?;

现在让我们回到有 2 个表:

UPDATE a JOIN d ON a.`id` = d.`id`
    SET d.upvotes = d.upvotes + (a.user = ?),
        d.allow = ?
    WHERE d.id = ? AND d.allow < ?;

这导致了另一种形式。 (不知道这样好不好)

UPDATE d
    SET d.upvotes = d.upvotes +
                    ( ( SELECT  user = ? FROM a WHERE a.id = d.id ) )
        d.allow = ?
    WHERE d.id = ? AND d.allow < ?;

或者,(同样可能没有任何显着差异):

UPDATE d
    SET d.upvotes = d.upvotes +
                EXISTS ( SELECT  user = ? FROM a WHERE a.id = d.id )
        d.allow = ?
    WHERE d.id = ? AND d.allow < ?;

【讨论】:

它不起作用:它正确更新允许我称之为 row 2 (其中id=?)但用户在每种情况下都保持不变(您的所有版本)。有关更多信息,请参阅我和 @Solarflare 之间的 cmets:我们找到了 some ideas,但它们都有一些问题,从 MariaDBMySQL 之间的兼容性到与您的类似的版本在某些情况下不起作用。我还想出了一些可行的方法(请参阅链接),但这似乎是一种解决方法而不是解决方案 @Foxel - 第一个“更新”没有显示user 需要更改;请相应地编辑它。【参考方案2】:

我已尝试为您的问题找到解决方案,并且可能有答案 我在这里正确理解了工作流程: 每个用户每天可以投票 1 次

    user1 点赞 user2 user1 今天已经投票了吗? ->是的:什么都不做 -> 否:将 user1 的日期和 +1 更改为 user2 的投票

在该示例中:“admin”、2、“2020-04-20”、2、“2020-04-20”

2 => 用户1 admin => user2 "2020-04-20" => 投票日期

我说的对吗? 如果是的话,我的解决方案

mariadb

UPDATE data SET upvotes = CASE WHEN id = (SELECT id FROM accountlist WHERE accountlist.user = ?)
                AND 
               (SELECT allow FROM data inner join accountlist on accountlist.id = data.id 
                 where accountlist.id = ? )< ?
            THEN upvotes + 1
            ELSE upvotes
          END,
allow = CASE WHEN id = ?
THEN ?
ELSE allow
END

mysql

UPDATE data
SET upvotes = CASE WHEN id = (SELECT id FROM (select * from accountlist) as al WHERE al.user = "bar")
                    AND 
                   (SELECT allow FROM (select * from data) as d inner join (select * from accountlist) as al1 on al1.id = d.id where al1.id = 1)<"2020-04-19"
                THEN upvotes + 1
                ELSE upvotes
              END,
allow = CASE WHEN id = 1
THEN "2020-04-19"
ELSE allow
END;

创建数据库:

         accountlist                       data
   |------------|---------------|    |------------|--------------|----------|
   | id         | user          |    | id         | allow        | upvotes  |
   |------------|---------------|    |------------|--------------|----------|
A: | 1          | admin         |    | 1          | 2020-04-18   | 2        |
B: | 2          | foo           |    | 2          | 2020-04-20   | 0        |
C: | 3          | bar           |    | 3          | 2020-04-22   | 1        |
   |------------|---------------|    |------------|--------------|----------|

具有价值: "admin", "2", "2020-04-20", "2", "2020-04-20" => 没有修改

“管理员”、“2”、“2020-04-22”、“2”、“2020-04-22”

         accountlist                       data
   |------------|---------------|    |------------|--------------|----------|
   | id         | user          |    | id         | allow        | upvotes  |
   |------------|---------------|    |------------|--------------|----------|
A: | 1          | admin         |    | 1          | 2020-04-18   | 3        |
B: | 2          | foo           |    | 2          | 2020-04-22   | 0        |
C: | 3          | bar           |    | 3          | 2020-04-22   | 1        |
   |------------|---------------|    |------------|--------------|----------|

"酒吧", 1, "2020-04-19", 1, "2020-04-19" =>

             accountlist                       data
   |------------|---------------|    |------------|--------------|----------|
   | id         | user          |    | id         | allow        | upvotes  |
   |------------|---------------|    |------------|--------------|----------|
A: | 1          | admin         |    | 1          | 2020-04-19   | 3        |
B: | 2          | foo           |    | 2          | 2020-04-22   | 0        |
C: | 3          | bar           |    | 3          | 2020-04-22   | 2        |
   |------------|---------------|    |------------|--------------|----------|

通常可以正确运行(在线测试),但使用 2 语句而不是 1 语句会更简单。

【讨论】:

它适用于 MariaDB,但不适用于 MySQL。奇怪的事实是我在 fiddle 上尝试过它,它似乎甚至可以与 MySQL 一起使用(它有点修改但与 your one 的结果相同。有什么建议吗? 我安装 wamp 服务器并测试它 在您的小提琴上,当您将 sql 版本从 8.0 更改为 5.7 时,请求失败。您的服务器使用哪个版本? 带有 phpMyAdmin 的 MySQL 5.6 我已经尝试过link,即使在 5.7 中也一切正常。 phpmyadmin 没有错误?

以上是关于SQL:如果 *condition* 在另一行上,则更新一行的主要内容,如果未能解决你的问题,请参考以下文章

SQL WHERE (conditions) AND (condition1 OR condition2) 不起作用

Mysql 为每一行检查是不是一个字段在另一个表中,如果不删除该行

SQL Server中的SUM,在JOIN的一侧有多行,另一侧在另一侧作为比较

BASH:试图在不引入空格的情况下在另一行上继续一个字符串[重复]

AutoHotkey 中的一行 if-condition-assignment

在另一条线上反向匹配多个场