对于数据输入项目,用户可以使用简写符号输入变量:
"Pour i1 into a flask."
"Warm the flask to 25 degrees C."
"Add 1 drop of i2 to the flask."
"Immediately seek cover."
在这种情况下,i1
和i2
是引用变量,其中数字是指成分。文本字符串位于INSTRUCTION
表中,其成分为INGREDIENT
表。
每种成分都有序列号用于分类。
用户可能会重新排列配料顺序,这会对指令产生不利影响。例如,成分订单最初可能如下所示:
seq | label
1 | water
2 | sodium
用户添加了另一种成分:
seq | label
1 | water
2 | sodium
3 | francium
用户重新排序列表:
seq | label
1 | water
2 | francium
3 | sodium
此时,以下行现在不正确:
"Add 1 drop of i2 to the flask."
i2
必须重新编号(因为成分#2移动到位置#3)以指向原始参考变量:
"Add 1 drop of i3 to the flask."
这是问题的简化版本。完整的问题可以有如下行:
"Add 1 drop of i2 to the o3 of i1."
o3
是一个对象(烧瓶),i1
和i2
分别是水和钠。
成分表的结构如下:
id | seq | label
指令表的结构如下:
step
我想到的算法:
step
匹配的所有'\mi([0-9]+)'
重复:编写的算法可能不正确。可能有两个必须改变的参考变量。先考虑一下:
seq | label
1 | water
2 | sodium
3 | caesium
4 | francium
在(交换钠和铯)之后:
seq | label
1 | water
2 | caesium
3 | sodium
4 | francium
每个步骤中的每个i2
必须变为i3
;同样,i3
必须成为i2
。所以
"Add 1 drop of i2 to the flask, but absolutely do not add i3."
变为:
"Add 1 drop of i3 to the flask, but absolutely do not add i2."
执行算法前两部分的代码类似于:
CREATE OR REPLACE FUNCTION
renumber_steps(
p_ingredient_id integer,
p_old_sequence integer,
p_new_sequence integer )
RETURNS void AS
$BODY$
DECLARE
v_tokens text[];
BEGIN
FOR v_tokens IN
SELECT
t.tokens
FROM (
SELECT
regexp_split_to_array( step, '\W' ) tokens,
regexp_matches( step, '\mi([0-9]+)' ) matches
FROM
instruction
) t
LOOP
RAISE NOTICE '%', v_tokens;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
什么是解决此问题的更有效方法(即,如何消除循环结构),可能利用PostgreSQL特定的功能,而不对数据模型进行重大修改?
谢谢!
PostgreSQL 9.1.2。
答案 0 :(得分:1)
您必须注意不要来回更改成分和seq
数字。为此目的,我为seq
引入了成分和负数的临时前缀,并在完成所有操作后将它们换成永久值。
可以像这样工作:
CREATE OR REPLACE FUNCTION renumber_steps(_old int[], _new int[])
RETURNS void AS
$BODY$
DECLARE
_prefix CONSTANT text := ' i'; -- prefix, incl. leading space
_new_prefix CONSTANT text := ' ###'; -- temp prefix, incl. leading space
i int;
o text;
n text;
BEGIN
IF array_upper(_old,1) <> array_upper(_new,1) THEN
RAISE EXCEPTION 'Array length mismatch!';
END IF;
FOR i IN 1 .. array_upper(_old,1) LOOP
IF _old[i] <> _new[i] THEN
o := _prefix || _old[i] || ' '; -- leading and trailing blank!
-- new instruction are temporarily prefixed with new_marker
n := _new_prefix || _new[i] || ' ';
UPDATE instruction
SET step = replace(step, o, n) -- replace all instances
WHERE step ~~ ('%' || o || '%');
UPDATE ingredient
SET seq = _new[i] * -1 -- temporarily negative
WHERE seq = _old[i];
END IF;
END LOOP;
-- finally replace temp. prefix
UPDATE instruction
SET step = replace(step, _new_prefix, _prefix)
WHERE step ~~ ('%' || _new_prefix || '%');
-- .. and temp. negative seq numbers
UPDATE ingredient
SET seq = seq * -1
WHERE seq < 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE STRICT;
呼叫:
SELECT renumber_steps('{2,3,4}'::int[], '{4,3,2}'::int[]);
算法要求......
......步骤中的成分由空格分隔
......没有永久的负面序号。
_old
和_new
是新旧instruction.seq
改变位置的成分的阵列。两个数组的长度必须匹配,否则将引发异常。它可以包含不会更改的seq
。什么都不会发生在那些人身上。
需要PostgreSQL 9.1 或更高版本。
答案 1 :(得分:0)
我认为您的模型存在问题...您应该拥有“真实姓名(id
)”(i1
,o3
等。)创建后修复并拥有第二个字段在提供“排序”的ingredient
表中。用户输入“排序名称”,并在将输入的数据保存到id
表中时立即将其替换为“真实姓名”(step
)。
当您从step
表中阅读时,您只需将当前“排序名称”替换/映射“真实姓名”(id
)用于显示目的,如果需要...
这样,每次有人更改排序时,您都不必更改step
表中的数据,这是一项复杂且昂贵的操作恕我直言 - 它也容易出现并发问题......
上面的选项将整个问题减少到INSERT / UPDATE / SELECT(表ingredient
)上的映射操作(表step
),用于当前正在处理的一个条目 - 它不会弄乱已存在的任何其他条目。