Создать функцию в postgresql для обновления значений столбцов из таблицы с предпочтительными значениями и псевдонимами - PullRequest
0 голосов
/ 19 сентября 2018

Я хочу создать функцию, которая будет обновлять столбец типа 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 голосов
/ 19 сентября 2018

Звучит так, будто вы хотите триггер на столе.Вот мое предложение:

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.

0 голосов
/ 19 сентября 2018

Вам не нужна функция;Вы можете просто обновить одну таблицу из содержимого другой таблицы:


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
        ;
...