这是一个有效的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
。
那么正确的用法是什么?
答案 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)
对于多表语法,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;