将逻辑添加到PostgreSQL存储过程

时间:2010-12-20 21:39:36

标签: postgresql stored-procedures database-schema

我正在使用PostgreSQL(8.3+)并定义了枚举和表格如下:

CREATE TYPE "viewer_action" AS ENUM ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H');

CREATE TABLE "preferences"  ( 
    "user_id"       integer NOT NULL,
    "item_id"       integer NOT NULL,
    "rating"        viewer_action NOT NULL,
    "time_created"  timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY ("user_id","video_id")
);

我还使用示例创建了一个存储过程,用于将新行插入到首选项表中 http://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#PLPGSQL-UPSERT-EXAMPLE

CREATE OR REPLACE FUNCTION add_preference(u INT, i INT, r viewer_action) RETURNS VOID AS $add_preference$
    BEGIN
        LOOP
            -- first try to update the key
            UPDATE preferences SET rating = r WHERE user_id = u AND item_id = i;
            IF found THEN
                RETURN;
            END IF;
            -- not there, so try to insert the key
            -- if someone else inserts the same key concurrently,
            -- we could get a unique-key failure
            BEGIN
                INSERT INTO preferences(user_id,item_id,rating) VALUES (u,i,r);
                RETURN;
            EXCEPTION WHEN unique_violation THEN
                -- do nothing, and loop to try the UPDATE again
            END;
        END LOOP;
    END;
$add_preference$ LANGUAGE plpgsql;

我需要在upsert中添加一些额外的逻辑,以防止某些值覆盖其他值。具体做法是:

  • A可以被B覆盖,可以被C覆盖,可以被D覆盖,依此类推。但B不能被A覆盖,也不能被B覆盖,等等。
  • F,G或H可以覆盖任何值,无论现有值是低还是高。

在伪代码中,这可能如下所示:

if (rating >= F) {
    insert;
} else if (rating > existing_rating) {
    insert;
} else {
    return;
}

3 个答案:

答案 0 :(得分:1)

在此表的INSERT OR UPDATE之前创建一个触发器。为此,您可以使用您的功能,只需稍作更改,例如将返回类型更改为TRIGGER。

在将行插入表之前触发BEFORE触发器,因此您可以在写入数据之前检查数据。

您可以在此处找到有关触发器的更多信息:
http://www.postgresql.org/docs/9.0/interactive/plpgsql-trigger.html

答案 1 :(得分:0)

要查看的方式是CASE http://www.postgresql.org/docs/8.2/static/functions-conditional.html#AEN13066,实现它​​应该不那么困难。正如@ szymon-guz建议的那样,将其置于触发器中。

答案 2 :(得分:0)

你不能用案例陈述做点什么吗?

像这样。

--If new rating is greater than existing rating update it else update to current value.
    UPDATE preferences
    SET rating = CASE WHEN r > rating THEN r ELSE rating END;