假设我有一张桌子:
Column | Type | Notes
---------+------------ +----------------------------------------------------------
id | integer | An ID that's FK to some other table
seq | integer | Each ID gets its own seq number
data | text | Just some text, totally irrelevant.
id
+ seq
是一个组合密钥。
我希望看到的是:
ID | SEQ | DATA
----+------ +----------------------------------------------
1 | 1 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
1 | 2 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
1 | 3 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
1 | 4 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
2 | 1 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
3 | 1 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
3 | 2 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
3 | 3 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
3 | 4 | Quick brown fox, lorem ipsum, lazy dog, etc etc.
如您所见,id
和seq
的组合是唯一的。
我不确定如何设置我的表(或插入语句?)来执行此操作。我想插入id
和data
,导致seq
成为依赖于id
的子序列。
答案 0 :(得分:19)
没问题!我们将制作两个表,things
和stuff
。 stuff
将是您在问题中描述的表格,things
就是它所引用的表格:
CREATE TABLE things (
id serial primary key,
name text
);
CREATE TABLE stuff (
id integer references things,
seq integer NOT NULL,
notes text,
primary key (id, seq)
);
然后我们将使用触发器设置things
,每次创建一行时都会创建一个新序列:
CREATE FUNCTION make_thing_seq() RETURNS trigger
LANGUAGE plpgsql
AS $$
begin
execute format('create sequence thing_seq_%s', NEW.id);
return NEW;
end
$$;
CREATE TRIGGER make_thing_seq AFTER INSERT ON things FOR EACH ROW EXECUTE PROCEDURE make_thing_seq();
现在我们最终会得到thing_seq_1
,thing_seq_2
等等......
现在stuff
上的另一个触发器,以便每次都使用正确的序列:
CREATE FUNCTION fill_in_stuff_seq() RETURNS trigger
LANGUAGE plpgsql
AS $$
begin
NEW.seq := nextval('thing_seq_' || NEW.id);
RETURN NEW;
end
$$;
CREATE TRIGGER fill_in_stuff_seq BEFORE INSERT ON stuff FOR EACH ROW EXECUTE PROCEDURE fill_in_stuff_seq();
这将确保当行进入stuff
时,id
列用于查找调用nextval
的正确序列。
以下是演示:
test=# insert into things (name) values ('Joe');
INSERT 0 1
test=# insert into things (name) values ('Bob');
INSERT 0 1
test=# select * from things;
id | name
----+------
1 | Joe
2 | Bob
(2 rows)
test=# \d
List of relations
Schema | Name | Type | Owner
--------+---------------+----------+----------
public | stuff | table | jkominek
public | thing_seq_1 | sequence | jkominek
public | thing_seq_2 | sequence | jkominek
public | things | table | jkominek
public | things_id_seq | sequence | jkominek
(5 rows)
test=# insert into stuff (id, notes) values (1, 'Keychain');
INSERT 0 1
test=# insert into stuff (id, notes) values (1, 'Pet goat');
INSERT 0 1
test=# insert into stuff (id, notes) values (2, 'Family photo');
INSERT 0 1
test=# insert into stuff (id, notes) values (1, 'Redundant lawnmower');
INSERT 0 1
test=# select * from stuff;
id | seq | notes
----+-----+---------------------
1 | 1 | Keychain
1 | 2 | Pet goat
2 | 1 | Family photo
1 | 3 | Redundant lawnmower
(4 rows)
test=#
答案 1 :(得分:15)
您可以使用window function分配SEQ
值,例如:
INSERT INTO YourTable
(ID, SEQ, DATA)
SELECT ID, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY DATA), DATA
FROM YourSource
答案 2 :(得分:2)
如果seq
反映(或应该反映)行的插入顺序,我宁愿使用自动填充的timestamp
并在运行时生成序列号。使用row_number()
选择行:
create table some_table
(
id integer not null,
inserted_at timestamp not null default current_timestamp,
data text
);
要获取seq
列,您可以执行以下操作:
select id,
row_number() over (partition by id order by inserted_at) as seq,
data
from some_table
order by id, seq;
与使用持久seq
列(特别是id, seq
上的索引)相比,select会慢一点。
如果这成为一个问题,您可以查看使用实体化视图,或添加seq
列,然后定期更新(我会不在触发性能原因)。
SQLFiddle示例:http://sqlfiddle.com/#!15/db69b/1
答案 3 :(得分:1)
您可以使用序列,它也是一个很好的选择
CREATE SEQUENCE SEQ_NUM START 1;
INSERT INTO TABLE_NAME(ID, SEQ, DATA) VALUES(**SOME_ID**, SELECT nextval('SEQ_NUM') AS SEQ_NUM, **SOME DATA**);
现在在这种情况下,序列" SEQ_NUM"价值将完全由PostgreSQL管理,现在你不必管理任何柜台或其他任何东西。
答案 4 :(得分:0)
只是一个猜测。
INSERT INTO TABLE (ID, SEQ, DATA)
VALUES
(
IDVALUE,
(SELECT max(SEQ) +1 FROM TABLE WHERE ID = IDVALUU),
DATAVALUE
);
答案 5 :(得分:0)
这是使用标准SQL的简单方法:
INSERT INTO mytable (id, seq, data)
SELECT << your desired ID >>,
COUNT(*) + 1,
'Quick brown fox, lorem ipsum, lazy dog, etc etc.'
FROM mytable
WHERE id = << your desired ID (same as above) >>;
请参阅SQL Fiddle Demo。
(如果你想要更聪明一点,你可以考虑创建一个trigger来在插入后立即使用相同的方法更新行。)
答案 6 :(得分:0)
我同样需要动态地存储树状结构,而不是一次添加所有ID。
我不希望每个组都使用顺序表,因为可能会有成千上万个。
它在密集的多处理环境中运行,因此必须具有竞争条件。
这里是第一级的插入功能。其他级别遵循相同的原则。
每个组作为独立的不可重复使用的顺序ID,该函数接收组名和子组名,为您提供现有ID或创建它并返回新ID。
我尝试了一个循环进行单选,但是代码又长又难读。
CREATE OR REPLACE FUNCTION getOrInsert(myGroupName TEXT, mySubGroupName TEXT)
RETURNS INT AS
$BODY$
DECLARE
myId INT;
BEGIN -- 1st try to get it if it already exists
SELECT id INTO myId FROM myTable
WHERE groupName=myGroupName AND subGroupName=mySubGroupName;
IF NOT FOUND THEN
-- Only 1 session can get it but others can read
LOCK TABLE myTable IN SHARE ROW EXCLUSIVE MODE;
-- 2nd try in case of race condition
SELECT id INTO myId FROM myTable
WHERE groupName=myGroupName AND subGroupName=mySubGroupName;
IF NOT FOUND THEN -- Doesn't exist. Get next ID for this group.
SELECT COALESCE(MAX(id), 0)+1 INTO myId FROM myTable
WHERE groupName=myGroupName;
INSERT INTO myTable (groupName, id, subGroupName)
VALUES (myGroupName, myId, mySubGroupName);
END IF;
END IF;
RETURN myId;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
要尝试:
CREATE TABLE myTable (GroupName TEXT, SubGroupName TEXT, id INT);
SELECT getOrInsert('groupA', 'subgroupX'); -- Returns 1
...
SELECT * FROM myTable;
groupname | subgroupname | id
-----------+--------------+----
groupA | subgroupX | 1
groupA | subgroupY | 2
groupA | subgroupZ | 3
groupB | subgroupY | 1
答案 7 :(得分:-1)
PostgreSQL支持分组的唯一列,如下:
CREATE TABLE example (
a integer,
b integer,
c integer,
UNIQUE (a, c)
);
见PostgreSQL Documentation - 第5.3.3节
简单: - )
答案 8 :(得分:-1)
我没有任何postgresql特定的经验,但你可以在insert语句中使用子查询吗?有点像,在Mysqlish中,
INSERT INTO MYTABLE SET
ID=4,
SEQ=( SELECT MAX(SEQ)+1 FROM MYTABLE WHERE ID=4 ),
DATA="Quick brown fox, lorem ipsum, lazy dog, etc etc."