7

我有两张桌子:

 CREATE TABLE "user"
    (
      username character varying(35) NOT NULL,
      CONSTRAINT user_pk PRIMARY KEY (username)
    )
    CREATE TABLE item
    (
      id serial NOT NULL,
      username character varying(35),
      user_item_number integer,
      item_value character varying(35),
      CONSTRAINT item_pk PRIMARY KEY (id),
      CONSTRAINT item_fk FOREIGN KEY (username)
          REFERENCES "user" (username) MATCH SIMPLE
          ON UPDATE NO ACTION ON DELETE NO ACTION,
      CONSTRAINT unique_item_username UNIQUE (username, user_item_number)
    )

在此处输入图像描述

我想user_item_number为每个username. 下图显示了示例。对于每个username: ( user1, user2)user_item_number从 1 开始并递增 1。 在此处输入图像描述

user_item_number我想我应该在插入之前使用一些触发器来获取插入的最大值username并增加它。但我不知道如何编写该触发器。

我也不知道如何考虑并发访问(并发插入多个具有相同值的行username)。我不想在插入两行相同的行时出现约束冲突错误usernameuser_item_number我希望触发器捕获该错误并再次增加 user_item_number值。

任何想法?

4

3 回答 3

4

生成和维护这样的无间隙序列真的很难。

获得相同结果的更好方法是使用窗口函数即时生成此类序列。就像是:

SELECT id, username, row_number() OVER (PARTITION BY username ORDER BY id) as user_item_number, item_value
from item_table;

它会给你想要的结果,并且不会导致任何并发问题。此外,它将始终保持序列无间隙。

于 2013-08-06T13:18:17.273 回答
1

Auto inc 应该是唯一的,并且每个表只有一个。

所以基于你似乎想要的

User(User_ID PK, ...)
UserItem(User_Item_ID PK, User_ID FK, ...)
UserItemValue(User_Item_Value_ID PK, User_Item_ID FK, ...)

从规范化的角度来看,您应该朝着这个方向前进

于 2013-08-06T12:26:02.527 回答
0

我找到了解决方案。我写了触发器和过程:

create OR REPLACE function myinsert() RETURNS trigger as $$
    BEGIN
        if NEW.user_item_number is not null then return NEW;
        end if;
        loop
            <<roolbac_to>>
            declare
                max INTEGER:=null;
            begin
            SELECT count(user_item_number) into max from item where username=NEW.username;
            if max is null then
                max:=1;
            ELSE
                max=max+1;
            end if;
            INSERT INTO item( username, user_item_number, item_value) VALUES (NEW.username,max, NEW.item_value);
            exit;
            exception WHEN unique_violation THEN
            --do nothing
            end;
        end loop;
    return null;
    end;

$$ LANGUAGE 'plpgsql';

CREATE TRIGGER trig1
 before insert 
    ON item
   FOR EACH ROW
EXECUTE PROCEDURE myinsert();

这个解决方案可以创建差距,但对我来说没问题。

我想用触发器而不是插入,但这是不可能的。所以我在插入触发器之前做了并返回null。在过程内部执行插入。操作说明:

if NEW.user_item_number is not null then return NEW;
    end if;

是不允许复发

于 2013-08-07T11:28:20.563 回答