mysql UPDATE需要按其他表中的数据进行ORDERED

时间:2013-01-14 18:27:17

标签: mysql sql sql-order-by sql-update

这是一个有效的SQL语句(实际上是两个语句) - 它采用一系列匹配行并添加delivery_number,每行增加一次:

SELECT @i:=0;
UPDATE pipeline_deliveries AS d
SET d.delivery_number = @i:=@i+1
WHERE d.pipelineID = 11
ORDER BY d.setup_time;

但是现在,客户不再希望它们按setup_time排序。他们需要根据出发时间进行订购,这是另一张表中的一个字段。我无法弄清楚如何做到这一点。

MySQL文档以及this answer表明,在4.0及更高版本(我们运行的是MySQL 5.0)中,我应该可以这样做:

SELECT @i:=0;
UPDATE pipeline_deliveries AS d RIGHT JOIN pipeline_routesXdeliveryID AS rXd
    ON d.pipeline_deliveryID = rXd.pipeline_deliveryID
LEFT JOIN pipeline_routes AS r
    ON rXd.pipeline_routeID = r.pipeline_routeID
SET d.delivery_number = @i:=@i+1
WHERE d.pipelineID = 11
ORDER BY r.departure_time,d.pipeline_deliveryID;

但我收到错误#1221 - Incorrect usage of UPDATE and ORDER BY

那么正确的用法是什么?

3 个答案:

答案 0 :(得分:2)

您不能混合UPDATE加入2个(或更多)表格和ORDER BY

您可以绕过限制,例如:

UPDATE 
    pipeline_deliveries AS upd
  JOIN
    ( SELECT t.pipeline_deliveryID, 
             @i := @i+1 AS row_number 
      FROM 
          ( SELECT @i:=0 ) AS dummy
        CROSS JOIN 
          ( SELECT d.pipeline_deliveryID
            FROM 
                pipeline_deliveries AS d 
              JOIN 
                pipeline_routesXdeliveryID AS rXd
                  ON d.pipeline_deliveryID = rXd.pipeline_deliveryID
              LEFT JOIN 
                pipeline_routes AS r
                  ON rXd.pipeline_routeID = r.pipeline_routeID
            WHERE 
                d.pipelineID = 11
            ORDER BY 
                r.departure_time, d.pipeline_deliveryID
          ) AS t
    ) AS tmp
      ON tmp.pipeline_deliveryID = upd.pipeline_deliveryID
SET 
    upd.delivery_number = tmp.row_number ;

以上使用了MySQL的两个特性,用户定义的变量和派生表内的排序。因为后者不是标准的SQL,所以它很可能在MySQL的功能版本中破解(当优化器足够巧妙地弄清楚派生表内的排序是无用的,除非有LIMIT子句)。实际上,查询将在最新版本的MariaDB(5.3和5.5)中完成。它会像ORDER BY不存在一样运行,结果不会是预期的结果。请参阅MariaDB网站上的相关问题: GROUP BY trick has been optimized away

在未来的主流MySQL版本中可能会发生同样的情况(可能在5.6,有人关心测试吗?),这将改进优化器代码。

因此,最好在标准SQL中编写它。最好的是尚未实现的窗口函数。但是你也可以使用自连接,只要你处理一小部分行受更新影响,效率就不会很差。

UPDATE 
    pipeline_deliveries AS upd
  JOIN
    ( SELECT t1.pipeline_deliveryID
           , COUNT(*) AS row_number
      FROM
          ( SELECT d.pipeline_deliveryID
                 , r.departure_time
            FROM 
                pipeline_deliveries AS d 
              JOIN 
                pipeline_routesXdeliveryID AS rXd
                  ON d.pipeline_deliveryID = rXd.pipeline_deliveryID
              LEFT JOIN 
                pipeline_routes AS r
                  ON rXd.pipeline_routeID = r.pipeline_routeID
            WHERE 
                d.pipelineID = 11
          ) AS t1
        JOIN
          ( SELECT d.pipeline_deliveryID
                 , r.departure_time
            FROM 
                pipeline_deliveries AS d 
              JOIN 
                pipeline_routesXdeliveryID AS rXd
                  ON d.pipeline_deliveryID = rXd.pipeline_deliveryID
              LEFT JOIN 
                pipeline_routes AS r
                  ON rXd.pipeline_routeID = r.pipeline_routeID
            WHERE 
                d.pipelineID = 11
          ) AS t2
          ON t2.departure_time < t2.departure_time
          OR t2.departure_time = t2.departure_time 
             AND t2.pipeline_deliveryID <= t1.pipeline_deliveryID
          OR t1.departure_time IS NULL
             AND ( t2.departure_time IS NOT NULL
                OR t2.departure_time IS NULL
                   AND t2.pipeline_deliveryID <= t1.pipeline_deliveryID
                 )
      GROUP BY
          t1.pipeline_deliveryID  
    ) AS tmp
      ON tmp.pipeline_deliveryID = upd.pipeline_deliveryID
SET 
    upd.delivery_number = tmp.row_number ;

答案 1 :(得分:1)

基于此documentation

  

对于多表语法,UPDATE更新每个名为的表中的行   在table_references中满足条件。在这种情况下,ORDER   不能使用BY和LIMIT。

在不了解MySQL的情况下,您可以打开游标并逐行处理,或者将其传递回您为处理此处理而维护的客户端代码(PHP,Java等)。

more digging之后:

  

要消除严重优化的子查询,需要重写   子查询作为连接,但你怎么能这样做并保留LIMIT和   订购?一种方法是在子查询中查找要更新的行   FROM子句,所以LIMIT和ORDER BY可以嵌套在   子查询。通过这种方式,work_to_do与十个联合起来   优先级最高的无人认领的行。通常你不能   在多表UPDATE中自行加入更新目标,但是因为它是   在FROM子句的子查询中,它适用于这种情况。

update work_to_do as target
   inner join (
      select w. client, work_unit
      from work_to_do as w
         inner join eligible_client as e on e.client = w.client
      where processor = 0
      order by priority desc
      limit 10
   ) as source on source.client = target.client
      and source.work_unit = target.work_unit
   set processor = @process_id;
  

有一个缺点:行未按主键顺序锁定。   这可能有助于解释我们在此表中偶尔出现的死锁

答案 2 :(得分:0)

艰难的道路: -


    ALTER TABLE eav_attribute_option 
        ADD temp_value TEXT NOT NULL 
        AFTER sort_order;
    UPDATE eav_attribute_option o
        JOIN eav_attribute_option_value ov ON o.option_id=ov.option_id 
        SET o.temp_value = ov.value 
        WHERE o.attribute_id=90;
    SET @x = 0;
    UPDATE eav_attribute_option 
        SET sort_order = (@x:=@x+1) 
        WHERE attribute_id=90 
        ORDER BY temp_value ASC;
    ALTER TABLE eav_attribute_option
        DROP temp_value;