在文本列中重新编号参考变量

时间:2012-01-06 10:38:51

标签: sql postgresql full-text-search plpgsql

背景

对于数据输入项目,用户可以使用简写符号输入变量:

"Pour i1 into a flask."
"Warm the flask to 25 degrees C."
"Add 1 drop of i2 to the flask."
"Immediately seek cover."

在这种情况下,i1i2是引用变量,其中数字是指成分。文本字符串位于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是一个对象(烧瓶),i1i2分别是水和钠。

表结构

成分表的结构如下:

id | seq | label

指令表的结构如下:

step

算法

我想到的算法:

  1. 对表达式step匹配的所有'\mi([0-9]+)'重复:
  2. 将步骤分解为单词代币。
  3. 对于每个令牌:
    1. 如果令牌的数字部分与旧序列号匹配,请将其替换为新序列号。
    2. 重新组合令牌并更新指令。
    3. 更新成分编号。
  4. 更新

    编写的算法可能不正确。可能有两个必须改变的参考变量。先考虑一下:

    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。

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)”(i1o3等。)创建后修复并拥有第二个字段在提供“排序”的ingredient表中。用户输入“排序名称”,并在将输入的数据保存到id表中时立即将其替换为“真实姓名”(step)。

当您从step表中阅读时,您只需将当前“排序名称”替换/映射“真实姓名”(id)用于显示目的,如果需要...

这样,每次有人更改排序时,您都不必更改step表中的数据,这是一项复杂且昂贵的操作恕我直言 - 它也容易出现并发问题......

上面的选项将整个问题减少到INSERT / UPDATE / SELECT(表ingredient)上的映射操作(表step),用于当前正在处理的一个条目 - 它不会弄乱已存在的任何其他条目。