【问题标题】:How do I combine two UPDATE statements in one MySQL query?如何在一个 MySQL 查询中组合两个 UPDATE 语句?
【发布时间】:2011-06-06 05:34:15
【问题描述】:

您好,

如何在一个查询中执行两个 UPDATE 语句,例如:

UPDATE albums SET isFeatured = '0' WHERE isFeatured = '1'

结合

UPDATE albums SET isFeatured = '1' WHERE id = '$id'

基本上,当精选新专辑时,之前精选的专辑会切换回正常状态,而新精选的专辑会设置为活动状态。

谢谢!

【问题讨论】:

    标签: mysql sql-update


    【解决方案1】:

    试试这个:

    UPDATE albums SET isFeatured = IF(id!='$id', '0','1')
    

    【讨论】:

    • 看起来这对我来说很活泼,易于理解且简短。谢谢:)
    • @a1ex07:我不知道,谢谢。它是否仍然会延长查询时间,因为它需要检查每一行?这可以通过索引来缓解吗?
    • 我认为它确实变慢了(虽然不是太多)。但是我们需要考虑到第一个(原始)查询会更新 `isFeatured = '1' 的所有内容。而且我很确定无论如何都涉及全表扫描 - 布尔(位)字段上的索引不能具有足够的选择性,无法在执行计划中考虑。
    • 答案是正确的,但如果(如您暗示的那样)表中只能有一张精选专辑,则数据模型未标准化。 +1 理查德哈里森的答案,以解决这个问题。
    • @Nate:我完全同意这个查询导致全面扫描。鉴于表结构,我想不出任何其他解决方案(不更改表)可以避免全表扫描。如果你能想到一个请告诉我。
    【解决方案2】:

    当你不得不做这种事情时,这表明你的数据模型是错误的,可以进行一些修复。

    所以,我建议添加一个单独的表 featured_albums(FK:int id_album)并使用它来确定专辑是否被推荐。

    你的更新变成了

    DELETE FROM featured_album; INSERT INTO featured_album SET id_album = $id;
    

    选择加入表

    SELECT album.id,
           album.name, 
           ( id_album IS NOT NULL ) AS isfeatured
    FROM   album
    LEFT JOIN featured_album ON id_album = album.id 
    

    根据对上述内容进行扩展的要求,基本上我建议添加一个表格,其中包含指示当前所选专辑的行。这是一对一的关系,即专辑表中的一条记录与 feature_albums 表中的一条相关记录。见Types of Relationship

    您从专辑表中删除 isFeatured 字段并添加一个新表。

    CREATE TABLE `featured_album` (
        `id_album` INTEGER NOT NULL,
        FOREIGN KEY (id_album) REFERENCES `album` (`id`)
    );
    

    DELETE FROM .. INSERT INTO 行通过在表中创建一个条目来设置精选专辑。

    带有 LEFT JOIN 的 SELECT 语句将拉入专辑表中的记录并连接从 features_album 表中匹配的记录,在我们的例子中,只有一条记录将匹配,因此在 features_album 表中有一个字段它将返回除精选专辑外的所有记录均为 NULL。

    如果我们这样做了

    SELECT album.id, album.name, featured_album.id_album as isFeatured0
    FROM   album
    LEFT JOIN featured_album ON id_album = album.id 
    

    我们会得到如下内容:

    +----+----------------+------------+
    | id | name           | isFeatured |
    +----+----------------+------------+
    |  1 | Rumours        |       NULL |
    |  2 | Snowblind      |       NULL |
    |  3 | Telegraph road |          3 |
    +----+----------------+------------+
    

    即isFeatured 的 NULL 或 ID。

    通过添加( id_album IS NOT NULL ) AS isfeatured 并使用我们得到的第一个查询

    +----+----------------+------------+
    | id | name           | isfeatured |
    +----+----------------+------------+
    |  1 | Rumours        |          0 |
    |  2 | Snowblind      |          0 |
    |  3 | Telegraph road |          1 |
    +----+----------------+------------+
    

    即isfeatured 的 0/1 使事情更具可读性,尽管如果您在 PHP 中处理结果,它不会对您的代码产生影响。

    【讨论】:

    • 我对这段代码很感兴趣,但绝不是 MySQL 专家(显然),你介意解释一下这里发生了什么吗?谢谢
    • 我已经扩展了我的答案;希望这是有道理的。
    • 非常感谢!我得试试这个,看起来不错
    【解决方案3】:

    您可以使用CASE WHEN 语句并记住在必要时设置原始值(下面的 ELSE 子句)并根据需要订购 CASE 条件(在下面的语句中,如果请求 id 的行也具有 isFeatured = 1,则 isFeatured 将为 0,以更改它交换 WHEN 子句)。

    UPDATE albums 
    SET isFeatured = CASE 
      WHEN isFeatured = '1' THEN '0' 
      WHEN id = '$id' THEN '1'
      ELSE isFeatured
    END
    

    【讨论】:

    • 这确实对我有帮助,将来会使用它。谢谢:)
    【解决方案4】:

    你做不到。您只能选择一组应该更新的记录,然后只能对所有记录执行一项操作。这是不可能的

     UPDATE x SET col1 = 1 WHERE col1 = 0 AND col1 = 0 WHERE col1 = 1;
    

    使用函数来解决这个问题时要小心,因为需要对每一行进行评估,这可能会变得非常昂贵。

    【讨论】:

    • +1:用于实现 MySQL 不会神奇地用 if 替换 where 子句来重写您的查询。
    【解决方案5】:

    MySQL 在 if 函数中无法使用索引: 您需要一个 MySQL 中不可能的函数索引。

    另请参阅:How does one create an index on the date part of DATETIME field in MySql

    我正在使用员工测试数据库http://dev.mysql.com/doc/employee/en/employee.html

    mysql> describe employees;
    +------------+---------------+------+-----+---------+-------+
    | Field      | Type          | Null | Key | Default | Extra |
    +------------+---------------+------+-----+---------+-------+
    | emp_no     | int(11)       | NO   | PRI | NULL    |       |
    | birth_date | date          | NO   |     | NULL    |       |
    | first_name | varchar(14)   | NO   |     | NULL    |       |
    | last_name  | varchar(16)   | NO   |     | NULL    |       |
    | gender     | enum('M','F') | NO   |     | NULL    |       |
    | hire_date  | date          | NO   |     | NULL    |       |
    +------------+---------------+------+-----+---------+-------+
    6 rows in set (0.01 sec)
    
    mysql> select count(*) from employees;
    +----------+
    | count(*) |
    +----------+
    |   300024 |
    +----------+
    1 row in set (0.37 sec)
    

    将所有性别设置为男性,以便模仿问题。

    mysql> update employees set gender = 'M';
    Query OK, 1 row affected (9.11 sec)
    Rows matched: 300024  Changed: 1  Warnings: 0
    
    mysql>  select emp_no, gender from employees order by emp_no limit 2;
    +--------+--------+
    | emp_no | gender |
    +--------+--------+
    |  10001 | M      |
    |  10002 | M      |
    +--------+--------+
    2 rows in set (0.00 sec)
    

    将一名员工设置为女性。 (注意它使用索引并且几乎是即时的。)

    mysql> update employees set gender = 'F' where emp_no = 10001;
    Query OK, 1 row affected (0.14 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    

    现在我们使用建议的答案。 (注意它不使用索引并触及每一行。)

    mysql> update employees set gender = if(emp_no=10002, 'F', 'M');
    Query OK, 2 rows affected (10.67 sec)
    Rows matched: 300024  Changed: 2  Warnings: 0
    

    索引会有帮助吗?

    > mysql> create index employees_gender_idx  on employees(gender);
    Query OK, 300024 rows affected (21.61 sec)
    Records: 300024  Duplicates: 0  Warnings: 0
    
    > mysql> update employees set gender = if(emp_no=10001, 'F', 'M');
    Query OK, 2 rows affected (9.02 sec)
    Rows matched: 300024  Changed: 2  Warnings: 0
    
    Nope.
    

    也有人说 MySQL 只会查看需要更改的行。

    mysql> update employees set gender = 'M';
    Query OK, 1 row affected (8.78 sec)
    Rows matched: 300024  Changed: 1  Warnings: 0
    

    猜不出来。如果使用WHERE 子句怎么办?

    mysql> update employees set gender = 'M' where gender ='F';
    Query OK, 0 rows affected (0.03 sec)
    Rows matched: 0  Changed: 0  Warnings: 0
    

    真快,现在它使用了索引。

    Mysql 不知道IF 函数会返回什么,并且必须 进行全表扫描。请注意,WHERE 确实表示位置,SET 确实表示设置。您不能指望 DB 只安排所有子句以获得良好的性能。

    正确的解决方案是发布两次更新(如果使用索引,这几乎是即时的。)

    注意,在别处说过 MySQL 会神奇地知道只更新它需要更改的行。

    【讨论】:

    • 非常详尽的解释,谢谢。所以,基本上,尝试合并这类事情实际上比发送两个单独的语句要慢?
    • 是的。某些数据库(例如 PostgreSQL)允许您对函数进行索引,但 MySQl 不允许。说您的表格未标准化(请参阅 Richard Harrisons 的回答)也是正确的,这可以解决您的问题。我只是想澄清错误信息。
    • 为了澄清,我添加了更多关于期望 MySQL 找出 SET 子句实际上是 where 子句的内容。
    【解决方案6】:

    为提供的优秀答案添加替代方法 @too where 而不是使用CASE IF 语句 -

    UPDATE album
        -> SET isFeatured = IF (
        -> isFeatured = '1', '0', IF (
        -> id = '$id', '1', isFeatured
        -> ));
    

    【讨论】:

      【解决方案7】:

      我认为你不能,或者至少不能以简洁或实用的方式。

      如果您想从 php/whatever 进行一次调用,那么您可以用分号分隔它们:

      UPDATE albums SET isFeatured = '0' WHERE isFeatured = '1';UPDATE albums SET isFeatured = '1' WHERE id = '$id';
      

      【讨论】:

        猜你喜欢
        • 2022-07-06
        • 1970-01-01
        • 2023-03-21
        • 2014-03-20
        • 1970-01-01
        • 1970-01-01
        • 2018-08-07
        • 2012-09-19
        相关资源
        最近更新 更多