好的,我正在PostgresQL中创建用户数据库,我已经使用了一两个月,但我不是很熟悉。目前,通过PostgresQL序列自动为每个注册用户分配一个唯一ID。这一直是惯例,并且一直很好,但是...... 我有来自客户端的请求,可以手动输入用户的某些ID。我预计不会超过300个用户,所以我为手动输入的ID保留了ID 500-600。 (如果他们在线注册,他们会得到自动递增的号码,很可能不会超过300.如果他们需要手动ID,他们只能获得500到600之间的预定ID。)是的,我肯定是99.9999%不会有超过300个“自动识别”用户。
我真的希望能够提供我想要的任何手册ID,然后如果他们在线注册并且已经使用了ID,他们将获得下一个可用ID。我意识到这会破坏序列的目的,但是,我不确定我的另一个选择是什么。我只能提供一定数量的ID,所以我宁可不只是'max + 1',如果可能的话 - 我想“填补空白”。我很确定答案在于手动输入ID时更新序列,但这听起来不错,不确定原因。
如果这不是一个好主意,只要说 - “嘿,你是个白痴。谁让你负责数据库呢?”我们都会开始我们的一天。谢谢你的时间。
答案 0 :(得分:3)
我可能完全DROP
序列,ALTER TABLE
从列中删除默认值。然后,在INSERT
期间,我写了类似的内容:
BEGIN;
LOCK TABLE users IN EXCLUSIVE MODE;
INSERT INTO users (user_id, blah, blah)
VALUES (
coalesce(
requested_id_or_null_if_none_supplied,
(SELECT coalesce(max(user_id),0) FROM users)+1
),
'blah',
'blah'
);
COMMIT;
这有很糟糕的并发性。正好一个事务可以在任何给定时刻插入用户。考虑到您正在使用的数量,只要您保持交易时间短,就应该完全没问题。
考虑添加一个用作公共显示标识符的新字段,与数据库的内部主键分开。使用此字段显示以满足客户的需要。让他们把他们想要的东西放进去,只需将它UNIQUE
声明为合适的唯一密钥的一部分。
如果这样做,您可以使用相同的序列保留基础数字分配,因此您不必更改引用数据库中其他位置的用户的方式。您无需更改应用程序的其余部分在内部引用用户的方式。显示用户标识符时,只需查找“用户编号”进行显示并使用即可。当用户输入用户号码时,查找匹配行的主键。输入/输出只需要用户编号作为最终用户的标识符,不会在数据库的其余部分引用它。
这就是为什么数据库/应用程序设计智慧强调你通常不应该向用户公开生成的密钥。最终,如果他们能够看到他们,他们将不可避免地希望能够通过“有趣”的结果来改变它们。更容易给他们自己的公共标识符。如果他们突然决定它应该是字母数字,但只能在星期二满月 - 那没关系,你就可以做到。
将“显示标识符”和“唯一内部行键”拆分为单独的内容后,您可以随意使用显示标识符执行任何操作,包括轻松更改它。
答案 1 :(得分:2)
您显然已经开始讲授如何正确使用序列 。你在这个问题上表现出了一些见解,@ Craig的答案全面地弥补了这些差距。
关于你的问题,我会谈论反过来:
为特殊客人预留一个特殊号码池。特殊数字应该简短
例如,10000
(根据您的情况)开始您的序列。无需节俭,序列号便宜且丰富。
CREATE TABLE usr (
usr_id serial PRIMARY KEY
,usr text -- UNIQUE??
);
-- Let sequence start at 10000
SELECT setval('usr_usr_id_seq', 10000, FALSE);
-- Init table with first user if you want to start at certain number
-- Else numbers start at the lowest manual entry.
INSERT INTO usr(usr_id, usr) VALUES (1, 'first_user');
特别INSERT (如果请求的号码不可用,请取第一个免费号码):
WITH x AS (SELECT 4::int AS usr_id, 'special_guest' AS usr)
INSERT INTO usr(usr_id, usr)
SELECT CASE
WHEN x.usr_id IS NULL THEN nextval('usr_usr_id_seq'::regclass)
WHEN EXISTS (SELECT 1 FROM usr u WHERE u.usr_id = x.usr_id) THEN (
SELECT u.usr_id + 1
FROM usr u
WHERE NOT EXISTS (SELECT 1 FROM usr u1
WHERE u1.usr_id = u.usr_id + 1)
ORDER BY u.usr_id
LIMIT 1)
ELSE x.usr_id
END
,x.usr
FROM x;
通用INSERT (从序列中取数字):
INSERT INTO usr(usr) VALUES ('unspecial_guest');
- > sqlfiddle demo
如果您需要担心并发性,那么您不应该首先使用它 此设置适用于特殊情况的手。
答案 2 :(得分:1)
我相信你的问题与序列无关。您可以创建一个产生负数的序列。这样您就可以完全分离手动和自动识别。但这并不能解决您的问题,因为您仍然需要找到积极ID中的“下一个可用数字”。
解决方案还取决于用户数量。如果您最多只有1000个用户。然后你可以用for循环确定“下一个更大的可用数字”。但是如果你有100万用户拥有随机(手动给定)标识符,那么问题就不那么容易了。
我不明白这里的概念。要么他们手动给出一个数字,要么他们没有。如果该号码已被占用,那么您为什么要找到最接近的号码?为什么它比序列返回的任何其他数字更好?我的意思是,如果已经使用了手动输入的号码,那么您可以向用户抛出错误消息。如果用户没有坚持给定的号码,那么任何其他号码都可以。正确?