在Postgresql中创建函数以使用首选值和别名更新表中的列值

时间:2018-09-19 17:09:33

标签: postgresql for-loop sql-update sql-function

我想创建一个函数,将varchar类型的列更新为另一个表的列中引用的首选字符串,以帮助我更迭代地清理此列。

CREATE TABLE big_table (
    mn_uid NUMERIC PRIMARY KEY,
    user_name VARCHAR
    );

INSERT INTO big_table VALUES
        (1, 'DAVE'),
        (2, 'Dave'),
        (3, 'david'),
        (4, 'Jak'),
        (5, 'jack'),
        (6, 'Jack'),
        (7, 'Grant'); 

CREATE TABLE nameKey_table (
    nk_uid NUMERIC PRIMARY KEY,
    correct VARCHAR,
    wrong VARCHAR
    );

INSERT INTO nameKey_table VALUES
        (1, 'David', 'Dave_DAVE_dave_DAVID_david'),
        (2, 'Jack', 'JACK_jack_Jak_jak');

我要执行以下过程:

UPDATE big_table
SET user_name = (SELECT correct
                 FROM nameKey_table 
                 WHERE wrong 
                 LIKE '%DAVE%')
WHERE user_name = 'DAVE';

但在user_name中的每个big_table上进行了循环,因此我有一个函数可以执行以下操作:

UPDATE big_table SET user_name = corrected_name_fn();

这是我尝试做的事情,但我似乎无法使其起作用:

CREATE FUNCTION corrected_name_fn() RETURNS VARCHAR AS $$
DECLARE entry RECORD;
DECLARE correct_name VARCHAR;
BEGIN 
FOR entry IN SELECT DISTINCT user_name FROM big_table LOOP
     EXECUTE 'SELECT correct 
              FROM nameKey_table
              WHERE wrong 
              LIKE ''%$1%''' 
              INTO correct_name
              USING entry;
            RETURN correct_name;
            END LOOP;
END;    
$$ LANGUAGE plpgsql;

我希望big_table中的最终输出是:

| mn_uid |  user_name |
|   1    | 'David'    |
|   2    | 'David'    |
|   3    | 'David'    |
|   4    | 'Jack'     |
|   5    | 'Jack'     |
|   6    | 'Jack'     |
|   7    | 'Grant'    |

我意识到第6行和第7行提供了两个我想用IF ELSE语句构建到函数中的独特情况。

  1. 如果user_name中有nameKey_table.correct,请转到下一个
  2. 如果user_name中没有nameKey_table.correct或与nameKey_table.wrong中的字符串不匹配,请保持原样。

谢谢您的帮助!

2 个答案:

答案 0 :(得分:0)

您不需要功能;您可以只从另一个表的内容中更新一个表:


UPDATE big_table dst
SET user_name = src.correct
FROM nameKey_table src
WHERE src.wrong LIKE '%' || dst.user_name || '%'
AND dst.user_name <> src.correct -- avoid idempotent updates
        ;

如果您需要性能,请不要依赖LIKE运算符,它不能使用索引来领先%。而是使用每行只有一个条目的查找表:


CREATE TABLE bad_spell (
    correct VARCHAR,
    wrong VARCHAR PRIMARY KEY -- This will cause an unique index to be created.
    );

INSERT INTO bad_spell VALUES
        ('David', 'Dave')
        ,('David', 'DAVE')
        ,('David', 'dave')
        ,('David', 'DAVID')
        ,('David', 'david')
        ,('Jack', 'JACK')
        ,('Jack', 'jack')
        ,('Jack', 'Jak')
        ,('Jack', 'jak')
        ;
-- This indexes could be temporary
CREATE INDEX ON big_table(user_name);

-- EXPLAIN
UPDATE big_table dst
SET user_name = src.correct
FROM bad_spell src
WHERE dst.user_name = src.wrong
AND dst.user_name <> src.correct -- avoid idempotent updates
        ;

SELECT* FROM big_table
        ;

答案 1 :(得分:0)

听起来您想在桌子上放一个触发器。这是我的建议:

CREATE OR REPLACE FUNCTION tf_fix_name() RETURNS TRIGGER AS
$$
DECLARE
    corrected_name TEXT;
BEGIN

    SELECT correct INTO corrected_name FROM nameKey_table WHERE expression ~* NEW.user_name;
    IF FOUND THEN
        NEW.user_name := corrected_name;
    END IF;

    RETURN NEW;
END;
$$
    LANGUAGE plpgsql;


CREATE TEMP TABLE big_table (
    mn_uid INT PRIMARY KEY,
    user_name TEXT NOT NULL
);

CREATE TRIGGER trigger_fix_name
    BEFORE INSERT
    ON big_table
    FOR EACH ROW
    EXECUTE PROCEDURE tf_fix_name();

CREATE TEMP TABLE nameKey_table (
    nk_uid INT PRIMARY KEY,
    correct TEXT NOT NULL,
    expression TEXT NOT NULL
    );

INSERT INTO nameKey_table VALUES
        (1, 'David', '(dave|david)'),
        (2, 'Jack', '(jack|jak)');

INSERT INTO big_table VALUES
        (1, 'DAVE'),
        (2, 'Dave'),
        (3, 'david'),
        (4, 'Jak'),
        (5, 'jack'),
        (6, 'Jack'),
        (7, 'Grant');

SELECT * FROM big_table;

+--------+-----------+
| mn_uid | user_name |
+--------+-----------+
|      1 | David     |
|      2 | David     |
|      3 | David     |
|      4 | Jack      |
|      5 | Jack      |
|      6 | Jack      |
|      7 | Grant     |
+--------+-----------+
(7 rows)

注意:我认为您可以使用不区分大小写的正则表达式轻松完成所需的操作。而且我还将您的主键更改为INT。不知道为什么它们是数字,但是并不能真正改变解决方案。我的解决方案是在PostgreSQL 9.6上开发和测试的。