mysql过程在更新前行时更新前行中的数字引用
Posted
技术标签:
【中文标题】mysql过程在更新前行时更新前行中的数字引用【英文标题】:mysql procedure to update numeric reference in previous rows when one is updated 【发布时间】:2011-01-28 01:53:44 【问题描述】:有一张这样的桌子
______________________ |编号 |标题 |订购 | |-------------------------| | 1 |测试1 | 1 | |-----|--------|--------| | 2 |测试2 | 2 | |-----|--------|--------| | 3 |测试3 | 3 | |-----|--------|--------| | 4 |测试4 | 4 | '------------'当我在我的 mysql shell 中引入对一行的单个更新时
$sql> UPDATE `table` SET order=1 WHERE id=3;
然后过程或方法重新采样之前更新较低值中的订单列,以使其订单像这样更新
______________________ |编号 |标题 |订购 | |-------------------------| | 1 |测试1 | 2 | |-----|--------|--------| | 2 |测试2 | 3 | |-----|--------|--------| | 3 |测试3 | 1 | |-----|--------|--------| | 4 |测试4 | 4 | '------------'任何帮助将不胜感激,谢谢!
【问题讨论】:
您的问题是什么?你想做什么?您是否试图让它们按 order 列的值的顺序返回? (按订单订购) 想法是(我认为):将id=3的订单号设置为1(从3开始);现在,重新排序新旧值之间的记录(order = 1, 2)以保留先前的顺序,但遵循 id=3。因此,当使用 'ORDER BY order' 选择数据时,序列将是 id = 3, 1, 2, 4。 我正在寻找一种更新订单值的过程或方法,以便根据更新后的值对其进行排序。我更新了一行,然后低于该行实际顺序且高于更新顺序的值将在其值中获得 +1 所以您正在更新 id 3 并且前面的行会在订单中添加一个?所以 id 1 是 1 + 1 现在是 2 id 2 是 2 + 1 现在是 3,但 id 4 不会改变,因为它在 id 3 之后。对吗? id 是数字和自动递增的吗? 【参考方案1】:对于所有与 threadstarter 有相同问题但不使用 IDS 作为 DBMS(如 @Jonathan Leffler)的人: 这是 MySQL 的这些过程中的一个 pastebin http://pastebin.com/AxkJQmAH
【讨论】:
欢迎来到 ***!请在您的答案中包含代码,以便每个人都可以访问它。有关更多提示,请参阅How to Answer。【参考方案2】:我认为有两种情况需要考虑:
-
移动一行,使其出现在排序的前面。
移动一行,使其在排序后出现。
无论哪种方式,这都不是微不足道的。不清楚“order”列是否存在唯一约束;最终结果当然应该具有唯一的顺序。
符号:
'On' 是指旧值中值为 'order = n' 的行 'Nn' 指的是新值中带有 'order = n' 的行在示例中(示例 1):
O3 --> N1 O1 --> N2 O2 --> N3作为替代方案,考虑移动 id = 2,使其具有 order = 4:
O2 --> N4 O3 --> N2 O4 --> N3您基本上是在“其他”行中添加或减去一个,其中那些是移动行的旧位置和移动行的新位置之间的旧顺序的行。在伪代码中,使用 $old 和 $new 来识别移动行的前后位置,并处理 case 1 ($old > $new):
UPDATE AnonymousTable
SET order = CASE
WHEN order = $old THEN $new
WHEN order >= $new AND order < $old THEN order + 1
END CASE
WHERE order BETWEEN $new AND $old;
案例2($old
UPDATE AnonymousTable
SET order = CASE
WHEN order = $old THEN $new
WHEN order > $new AND order <= $old THEN order - 1
END CASE
WHERE order BETWEEN $old AND $new;
鉴于整个 UPDATE 上的 WHERE 子句,您可以删除 CASE 中的第二个 WHEN 并用简单的 ELSE 替换它。
UPDATE AnonymousTable
SET order = CASE
WHEN order = $old THEN $new
ELSE order + 1
END CASE
WHERE order BETWEEN $new AND $old;
UPDATE AnonymousTable
SET order = CASE
WHEN order = $old THEN $new
ELSE order - 1
END CASE
WHERE order BETWEEN $old AND $new;
我认为存储过程是有序的 - 根据输入参数 $old、$new 在两个语句之间进行选择。您可能可以通过明智地组合表达式来做一些事情,例如 '($old - $new) / ABS($old - $new)
' 和 'MIN($old, $new)
' 和 'MAX($old, $new)
' 其中 MIN/MAX 不是聚合,而是一对值的比较器函数(如在 Fortran 和其他编程语言中找到)。
请注意,我假设在执行单个 SQL 语句时,不会强制执行唯一性约束(如果有),因为每一行都发生了更改 - 仅当语句完成时。这是必要的,因为您实际上无法控制处理行的顺序。我知道这会导致麻烦的 DBMS;我知道其他人不会。
这一切都可以在一条 SQL 语句中完成 - 但您确实需要一个存储过程来整理语句的参数。我使用 IBM Informix Dynamic Server(MacOS X 10.6.2 上的 11.50.FC6),这是在语句末尾对“订单”列强制执行唯一约束的 DBMS 之一。我在没有 UNIQUE 约束的情况下开发了 SQL;当然,这也有效。 (是的,IDS 确实允许您回滚 DDL 语句,例如 CREATE TABLE 和 CREATE PROCEDURE。您说什么?您的 DBMS 不允许?多么古怪!)
BEGIN WORK;
CREATE TABLE AnonymousTable
(
id INTEGER NOT NULL PRIMARY KEY,
title VARCHAR(10) NOT NULL,
order INTEGER NOT NULL UNIQUE
);
INSERT INTO AnonymousTable VALUES(1, 'test1', 1);
INSERT INTO AnonymousTable VALUES(2, 'test2', 2);
INSERT INTO AnonymousTable VALUES(3, 'test3', 3);
INSERT INTO AnonymousTable VALUES(4, 'test4', 4);
SELECT * FROM AnonymousTable ORDER BY order;
CREATE PROCEDURE move_old_to_new(old INTEGER, new INTEGER)
DEFINE v_min, v_max, v_gap, v_inc INTEGER;
IF old = new OR old IS NULL OR new IS NULL THEN
RETURN;
END IF;
LET v_min = old;
IF new < old THEN
LET v_min = new;
END IF;
LET v_max = old;
IF new > old THEN
LET v_max = new;
END IF;
LET v_gap = v_max - v_min + 1;
LET v_inc = (old - new) / (v_max - v_min);
UPDATE AnonymousTable
SET order = v_min + MOD(order - v_min + v_inc + v_gap, v_gap)
WHERE order BETWEEN v_min AND v_max;
END PROCEDURE;
EXECUTE PROCEDURE move_old_to_new(3,1);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(1,3);
SELECT * FROM AnonymousTable ORDER BY order;
INSERT INTO AnonymousTable VALUES(5, 'test5', 5);
INSERT INTO AnonymousTable VALUES(6, 'test6', 6);
INSERT INTO AnonymousTable VALUES(7, 'test7', 7);
INSERT INTO AnonymousTable VALUES(8, 'test8', 8);
EXECUTE PROCEDURE move_old_to_new(3,6);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(6,3);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(7,2);
SELECT * FROM AnonymousTable ORDER BY order;
EXECUTE PROCEDURE move_old_to_new(2,7);
SELECT * FROM AnonymousTable ORDER BY order;
ROLLBACK WORK;
数字反转的存储过程的调用对每次都恢复原始顺序。显然,我可以重新定义v_inc
变量,使其不再是±1,而是'LET v_inc = v_inc - v_min + v_gap;
',然后MOD 表达式将只是'MOD(order + v_inc, v_gap)
'。我没有检查这是否适用于负数。
对 MySQL 或其他 DBMS 的适应留给读者作为练习。
【讨论】:
我会尝试,但它看起来像我正在寻找的解决方案,我会在它工作时检查解决,非常感谢! 使用“between”仅对您要更改的范围内的行重新编号是个好主意。 在 mysql(他似乎正在使用)中,随着每一行的更改,唯一性约束被强制执行,但是您可以通过放置“ORDER BY”来控制处理顺序“在更新。 @David Gelhar:这将使生活变得困难-您必须“双重更新”某些行以将其“移开”(可能会否定其订单价值?)以便您可以替换其他值,然后将“不碍事”行移动到最终位置。通常,单个 SQL 语句不会将单个行更新两次。【参考方案3】:假设你传入了要更改的行的id为change_row_id,新的订单为new_order:
current_order = SELECT order from 'table' WHERE id=change_row_id;
if (current_order > new_order)
UPDATE `table` SET order=order+1 WHERE (order > new_order AND
order < current_order);
else
[you'll have to figure out how you want to handle this. Do you want the
orders to all be sequential with no breaks?]
ENDYIF;
UPDATE 'table' SET order=new_order WHERE id=change_row_id;
我不知道mysql,所以你可能需要调整sql。而且您肯定希望在单个事务中执行此操作。要么提交所有内容,要么什么都不提交。
【讨论】:
如果他想降低一个元素的顺序怎么办?即:从订单3到4【参考方案4】:另一种方法是使用浮点数而不是整数进行排序。在此设置中,您只需在更改排序时更新一行。让我们从这个开始:
id order
1 1
2 2
3 3
4 4
现在您想更改顺序,使项目 2 出现在项目 3 和 4 之间。您所要做的就是更新项目 2,使其新的order
是一个介于 3 和 4 之间的值,例如 3.5:
id order
1 1
2 3.5
3 3
4 4
【讨论】:
开箱即用的有趣思考! +1 表示有趣的方法 -1 表示在按浮点数排序时糟糕的数据库性能【参考方案5】:伪代码:
CREATE TRIGGER reorder AFTER UPDATE ON `table`
FOR EACH ROW BEGIN
UPDATE `table` SET order=order+1 WHERE id < 3 ORDER BY id DESC LIMIT 1;
END;
|
delimiter ;
上一个 ID:
UPDATE `table` SET order=order+1 WHERE id < 3 ORDER BY id DESC LIMIT 1;
【讨论】:
【参考方案6】:也许做 2 条更新语句:
UPDATE `table` SET ord=ord+1 WHERE ord >= 1 order by ord desc;
UPDATE `table` SET ord=1 WHERE id=3;
(可能希望将这两个操作组合成一个事务,而不是使用自动提交)
编辑:在第一个更新中添加“order by”以控制更新顺序,避免“重复键问题”。(同时,避免列名中的“order”关键字:-)
【讨论】:
这从 4 移动到 5。如果订单有唯一约束,则违反该约束,因此第一个语句失败。 Jonathan Leffler 是对的,但我看到了一个可以尝试的模式,在 where 子句中使用 2 个约束。我会尝试一下,我会在这里告诉你结果,谢谢! 您可以通过在 UPDATE 上放置“order by”来控制更新发生的顺序。请参阅dev.mysql.com/doc/refman/5.1/en/update.html 中的示例【参考方案7】:我猜你想要达到的效果最好不要使用数据库查询,而是使用简单的排序构造。
您应该将数据库记录保存为集合或某种结构/数组/列表中的对象,这取决于您选择的语言。
然后您创建一个简单的排序算法,根据修改其他行的所有顺序的顺序对所有记录进行排序,并创建一个在传递相同的集合后自动更新所有修改的行的过程。
【讨论】:
以上是关于mysql过程在更新前行时更新前行中的数字引用的主要内容,如果未能解决你的问题,请参考以下文章
菜鸟的Xamarin.Forms前行之路——各种报错问题解决方法合集(不定时更新)
Oracle sql 错误 ora-01722 无效数字 ora-02063 前行来自