Sql 具有交替序列的列
我想创建一个user_widgets表,该表由user_id和user_widget_id主键控制,其中user_widget_id的工作方式类似于一个序列,只是它从每个用户1开始 是否有一个通用或实用的解决方案?我使用的是PostgreSQL,但如果能提供一个不可知论的解决方案,我将不胜感激 示例表:用户_小部件Sql 具有交替序列的列,sql,postgresql,database-design,auto-increment,Sql,Postgresql,Database Design,Auto Increment,我想创建一个user_widgets表,该表由user_id和user_widget_id主键控制,其中user_widget_id的工作方式类似于一个序列,只是它从每个用户1开始 是否有一个通用或实用的解决方案?我使用的是PostgreSQL,但如果能提供一个不可知论的解决方案,我将不胜感激 示例表:用户_小部件 | user_id | user_widget_id | user_widget_name | +-----------+------------------+-
| user_id | user_widget_id | user_widget_name |
+-----------+------------------+----------------------+
| 1 | 1 | Andy's first widget |
+-----------+------------------+----------------------+
| 1 | 2 | Andy's second widget |
+-----------+------------------+----------------------+
| 1 | 3 | Andy's third widget |
+-----------+------------------+----------------------+
| 2 | 1 | Jake's first widget |
+-----------+------------------+----------------------+
| 2 | 2 | Jake's second widget |
+-----------+------------------+----------------------+
| 2 | 3 | Jake's third widget |
+-----------+------------------+----------------------+
| 3 | 1 | Fred's first widget |
+-----------+------------------+----------------------+
编辑:
我只是想为这个设计加入一些理由
1。减少信息披露,而不仅仅是“通过默默无闻实现安全”
在一个系统中,用户不应该知道彼此,他们也不应该知道彼此的widget_id。如果这是一张清单、奇怪的商业秘密、发票或其他更敏感的东西的表格,他们就可以开始为这些小部件设置自己的未受影响的ID。除了明显的例行安全检查之外,这还添加了一个隐式安全层,在该层中,表必须通过小部件id和用户id进行过滤
2。数据导入
应该允许用户从其他系统导入数据,而不必丢弃所有遗留ID(如果他们有整数ID)
3。清洁度
与我的第一点并没有太大的不同,但我认为创建的内容比其他人少的用户可能会对他们的widget ID的显著跳跃感到困惑或恼火。这当然是表面的,而不是功能性的,但仍然有价值
可能的解决方案
其中一个答案表明,应用程序层可以处理这个问题。我可以在该用户的表中存储一个递增的下一个_id列。或者甚至可以只计算每个用户的行数,而不允许删除记录(改为使用删除/停用标志)。这可以通过触发器函数,甚至是存储过程而不是在应用程序层完成吗?如果您有一个表:
CREATE TABLE user_widgets (
user_id int
,user_widget_name text --should probably be a foreign key to a look-up table
PRIMARY KEY (user_id, user_widget_name)
)
您可以动态分配user\u widget\u id
并查询:
WITH x AS (
SELECT *, row_number() OVER (PARTITION BY user_id
ORDER BY user_widget_name) AS user_widget_id
FROM user_widgets
)
SELECT *
FROM x
WHERE user_widget_id = 2;
user\u widget\u id
在此场景中按每个用户的字母顺序应用,并且没有间隙,添加、更改或删除条目显然会导致更改
更多关于
稍微更稳定(但不是完全稳定): 以及:
解决问题更新 您可以实现一个机制来统计每个用户的现有小部件。但是,要使并发写操作防弹,您将很难。您必须锁定整个表或使用-这两个都是真正的性能下降,需要额外的代码 但是,如果您保证不删除任何行,那么您可以使用我的第二种方法-在整个表中为
user\u widget\u id
设置一个序列,这将为您提供一个“原始”id。序列是一个经验证的并发加载解决方案,保留了user\u widget\u id
中的相对顺序,而且速度很快。您可以使用视图提供对表的访问,该视图动态地将“原始”user\u widget\u id
替换为相应的user\u widget\u nr
,就像我上面的查询一样
您还可以(另外)在非工作时间或由您选择的事件触发,通过将其替换为user\u widget\u nr
来“具体化”一个无间隙的user\u widget\u id
为了提高性能,我会让user\u widget\u id
的序列从一个非常高的数字开始。似乎每个用户只能有几个小部件
SELECT setval(user_widgets_user_widget_id_seq', 100000);
如果没有足够高的数字可以确保安全,则添加一个标志。使用user\u widget\u id>100000的条件快速识别“原始”id。如果您的表很大,您可能需要使用条件(将是小的)添加一个表。用于案例中提到的视图中
语句。在此语句中,要“具体化”ID:
可能在非工作时间使用REINDEX
或甚至真空全分析用户小部件
。考虑低于100,因为列将至少更新一次。
我当然不会把这件事留给应用程序处理。这会带来多个额外的失败点。我将加入进来,对具体要求提出质疑。一般来说,如果您试图对这类事情进行排序,最好由应用程序来完成。如果你认识我,你就会意识到这是在说些什么。我担心的是,我所能想到的每一个案例都可能需要申请者重新排序,因为否则这些数字就无关紧要了
所以我只想:
CREATE TABLE user_widgets (
user_id int references users(id),
widget_id int,
widget_name text not null,
primary key(user_id, widget_id)
);
我就不说了
现在根据您的理由,这解决了您所有的问题(导入)。然而,我曾经在很长一段时间内做过类似的事情。我遇到的用例是一个地方税务管辖区要求装箱单(!)与发票分开,按顺序编号,没有空白。计数记录,顺便说一句,这不符合您的进口要求
我们所做的是创建一个表,每个序列有一行,并使用它,然后将其与触发器绑定。这些是数据库中的ID。我想不出任何实际的理由来强制存储的ID遵循这种模式。现在,如果您希望以这种方式对它们进行编号以便于显示,有几种方法可以在不更改基础数据的情况下解决这一问题。那么如何通过这些参数查询表?您可以为如何尝试查询的问题添加更多详细信息吗?我仍然认为没有必要强制对存储的ID进行任意排序。我在回答中添加了一点内容以解决您的更新问题。我倾向于同意这一点,但我确实希望在数据库中管理它。我已经用更多的细节更新了我的问题。我已经适当地更新了我的答案。当然,计算记录以确定id将无法满足从客户导入任意整数id的要求。我建议将其放在应用程序层t中的主要原因
SELECT setval(user_widgets_user_widget_id_seq', 100000);
UPDATE user_widgets w
SET user_widget_id = u.user_widget_nr
FROM (
SELECT user_id, user_widget_id
,row_number() OVER (PARTITION BY user_id
ORDER BY user_widget_id) AS user_widget_nr
FROM user_widgets
WHERE user_widget_id > 100000
) u
WHERE w.user_id = u.user_id
AND w.user_widget_id = u.user_widget_id;
CREATE TABLE user_widgets (
user_id int references users(id),
widget_id int,
widget_name text not null,
primary key(user_id, widget_id)
);