MySQL更新组内最大值的所有记录

时间:2017-01-30 07:05:16

标签: mysql

道歉,如果这已在其他地方得到解答,我只是没有看到它;这是我发现的最接近的,但它并不是我想要做的。

MySQL - updating all records to match max value in group

我在生产Web服务器上有一个表,大约有15,000行。有许多记录与另一个记录共享item_name,但item_meters是(通常但不总是)每行的唯一值。 item_id始终是唯一的。目前,每条记录的值都为" 0"在item_flag列中。

我想更新每个item_name组中具有最大item_meters值的所有记录,以使item_flag值为" 1"。

以下是按item_id ASC排序的表格的简化版本:

----------------------------------------------
                  mytable
----------------------------------------------
item_id | item_name | item_meters | item_flag
--------+-----------+-------------+-----------
001     | aaa       | 224         | 0
002     | aaa       | 359         | 0
003     | aaa       | 456         | 0
004     | bbb       | 489         | 0
005     | bbb       | 327         | 0
006     | bbb       | 215         | 0
007     | ccc       | 208         | 0
008     | ccc       | 756         | 0
009     | ccc       | 756         | 0
--------+-----------+-------------+-----------

期望的结果将是" 1"在每个" aaa"的item_flag列中拥有最大的item_meters,每个" bbb"拥有最大的item_meters,每个" ccc"拥有最大的item_meters等等:

----------------------------------------------
                  mytable
----------------------------------------------
item_id | item_name | item_meters | item_flag
--------+-----------+-------------+-----------
001     | aaa       | 224         | 0
002     | aaa       | 359         | 0
003     | aaa       | 456         | 1
004     | bbb       | 489         | 1
005     | bbb       | 327         | 0
006     | bbb       | 215         | 0
007     | ccc       | 208         | 0
008     | ccc       | 756         | 1
009     | ccc       | 756         | 0
--------+-----------+-------------+-----------

(如果有2个或更多记录具有相同的item_name 相同的item_meters(例如上面的item_id 008和009),则所需的结果将是具有数字较低的item_id的记录( item_id始终是唯一的,item_flag值为" 1"而具有数字更高的item_id的行仍然具有item_flag值" 0")

另外值得注意的是,即使此数据库在生产Web服务器后面运行,每天都会添加新行,但每次添加新行时都不需要更新表。无论以后是否在参数之外添加新行,它都只需要一次。我提到这个的原因是因为执行速度不是一个大问题,因为查询只会被执行一次。

提前谢谢!如果我能以任何方式提供更多信息或澄清我的问题,请告诉我。

1 个答案:

答案 0 :(得分:2)

我采取的方法是首先编写一个查询(SELECT语句),它将返回我们想要更新的行的item_id值。

作为起点,获取item_meters的最大值,这是一个简单的查询:

  SELECT m.item_name
       , MAX(m.item_meters) AS max_item_meters
    FROM my_table m
   GROUP BY m.item_name

我们可以将该查询用作另一个查询中的内联视图,以便为每个item_id

获取最低item_name
  SELECT MIN(o.item_id) AS min_item_id
    FROM ( SELECT m.item_name
                , MAX(m.item_meters) AS max_item_meters
            FROM my_table m
           GROUP BY m.item_name
         ) n
    JOIN my_table o
      ON o.item_name = n.item_name
     AND o.item_meters = n.max_item_meters
   GROUP BY o.item_name, o.item_meters

我们可以将该查询用作内联视图,以获取与我们返回的item_id值相关联的整行...

  SELECT t.item_id
       , t.item_name
       , t.item_meters
       , t.item_flag
    FROM my_table t
    JOIN ( SELECT p.min_item_id
             FROM ( SELECT MIN(o.item_id) AS min_item_id
                      FROM ( SELECT m.item_name
                                  , MAX(m.item_meters) AS max_item_meters
                               FROM my_table m
                              GROUP BY m.item_name
                           ) n
                      JOIN my_table o
                        ON o.item_name = n.item_name
                       AND o.item_meters = n.max_item_meters
                     GROUP BY o.item_name, o.item_meters
                  ) p
         ) q
      ON q.min_item_id = t.item_id

SELECT查询工作后,将其转换为UPDATE语句...将SELECT ... FROM替换为UPDATE,并添加SET子句。 (有时,将内联视图包装在另一个SELECT中是必要的,以避免MySQL错误地禁止对我们正在更新的表的引用。)

  UPDATE my_table t
    JOIN ( SELECT p.min_item_id
             FROM ( SELECT MIN(o.item_id) AS min_item_id
                      FROM ( SELECT m.item_name
                                  , MAX(m.item_meters) AS max_item_meters
                               FROM my_table m
                              GROUP BY m.item_name
                           ) n
                      JOIN my_table o
                        ON o.item_name = n.item_name
                       AND o.item_meters = n.max_item_meters
                     GROUP BY o.item_name, o.item_meters
                  ) p
         ) q
      ON q.min_item_id = t.item_id
     SET t.item_flag = '1' 

如果目的不是更新现有表,而是返回结果集,我们可以编写查询并对同一个内联视图执行外连接,并为item_flag返回0或1 ,测试item_id是否匹配我们想要标记为1 ...

  SELECT t.item_id
       , t.item_name
       , t.item_meters
       , IF(q.min_item_id IS NULL,0,1) AS `item_flag`
    FROM my_table t
    LEFT
    JOIN ( SELECT p.min_item_id
             FROM ( SELECT MIN(o.item_id) AS min_item_id
                      FROM ( SELECT m.item_name
                                  , MAX(m.item_meters) AS max_item_meters
                               FROM my_table m
                              GROUP BY m.item_name
                           ) n
                      JOIN my_table o
                        ON o.item_name = n.item_name
                       AND o.item_meters = n.max_item_meters
                     GROUP BY o.item_name, o.item_meters
                  ) p
         ) q
      ON q.min_item_id = t.item_id