Sql 在列上强制唯一性但允许某些重复项的最佳做法?
我想弄明白的是:应该有一个表来存储新客户管理系统的授权,每个授权都有其唯一的标识符。这个约束很容易转换为SQL,但不幸的是,由于官僚作风的缓慢,有时我们需要创建一个带有占位符ID(例如,“temp”)的条目,以便客户端能够开始接受服务 实施此条件唯一性约束的最佳实践是什么 以下是我有限的经验所能提供的:Sql 在列上强制唯一性但允许某些重复项的最佳做法?,sql,postgresql,database-design,unique-index,Sql,Postgresql,Database Design,Unique Index,我想弄明白的是:应该有一个表来存储新客户管理系统的授权,每个授权都有其唯一的标识符。这个约束很容易转换为SQL,但不幸的是,由于官僚作风的缓慢,有时我们需要创建一个带有占位符ID(例如,“temp”)的条目,以便客户端能够开始接受服务 实施此条件唯一性约束的最佳实践是什么 以下是我有限的经验所能提供的: 使用Postgresql手册(->)中提到的部分索引。它还提到,当成功的测试很少而失败的测试很多时,这是一种特别有效的方法。在我们将要迁移的遗留数据库中,每月有130000行和大约5个临时授权
- 使用Postgresql手册(->)中提到的部分索引。它还提到,
当成功的测试很少而失败的测试很多时,这是一种特别有效的方法。在我们将要迁移的遗留数据库中,每月有130000行和大约5个临时授权,但整个表每年仅增长约200行。这是正确的方法吗?(我也不确定在这种情况下“高效”是什么意思。)
- 为临时授权创建一个单独的表,但它会复制表结构
- 为一组列定义一个唯一约束。授权是针对特定时间段内的特定服务向个人发出的授权
编辑:
很抱歉,我认为我对授权ID的描述有点模糊:它是由国务院提供的,格式为
NMED01245678
,并且是手工输入的。它是唯一的,但有时只在以后提供。这对于评论来说有点长
我想我会有一个具有唯一授权的授权表。然后,授权可以有两种类型:“批准”和“临时”。你可以用两列来处理这个问题
但是,我可能会将授权id作为一个序列列,而“approved”id是表中的一个字段。该表可能具有唯一的约束。您可以使用完整的unique
约束或带有筛选值的唯一约束(Postgres允许在唯一约束中使用多个NULL
值,但第二个约束更明确)
对于临时授权,您可以使用相同的过程——使用不同的列。大概您有一些机制来授权它们并存储批准日期、时间和人员
我不会用两张桌子。授权分散在多个表中似乎会造成混乱。在代码中的任何地方,如果您想查看谁拥有授权,都有可能误读数据。有一种简单、快速且安全的方法: 添加一个布尔列以标记默认为空的临时条目,例如:
temp bool DEFAULT NULL CHECK (temp)
添加的检查约束不允许FALSE
,只允许NULL
或TRUE
。默认空值的存储成本通常为。。。无-除非行中没有其他空值
CREATE UNIQUE INDEX tbl_unique_id_uni ON tbl (unique_id) WHERE temp IS NULL;
这只包括应该是唯一的行。索引大小根本没有增加。
确保将temp为NULL的谓词添加到应该使用唯一索引的查询中
相关的:
您可以有几种可能性:
使临时标识符唯一;例如,如果它们是自动创建的(而不是手动输入),请执行以下操作:
CREATE SEQUENCE temp_ids_seq ; -- This done only once for the database
每当您需要新的临时id时,请发出
'temp' || nxtval('temp_ids_seq') AS id
使用部分索引,假设允许的值为temp
CREATE UNIQUE INDEX tbl_unique_idx ON tbl (id) WHERE (id IS DISTINCT FROM 'temp')
为了提高效率,在这些情况下,您可能还希望有补充索引:
CREATE INDEX tbl_temp_idx ON tbl (id) WHERE (id IS NOT DISTINCT FROM 'temp')
最后一个索引将帮助查询查找id='temp'
在我看来,不建议将远程密钥用作主键(部分主键)
- 它们不在你的控制之下;他们可以改变
- 您不能保证正确性和/或唯一性(电子邮件地址、电话号码、许可证号码、序列号)
- 将它们用作PK将导致它们被用作此表中其他表的FK,fat索引和批在更改时级联
\i tmp.sql
CREATE TABLE the_persons
( seq SERIAL NOT NULL PRIMARY KEY -- surrogate key
, registrationnumber varchar -- "remote" KEY, not necesarily UNIQUE
, is_validated BOOLEAN NOT NULL DEFAULT FALSE
, last_name varchar
, dob DATE
);
CREATE INDEX name_dob_idx ON the_persons(last_name, dob)
;
CREATE UNIQUE INDEX registrationnumber_idx ON the_persons(registrationnumber,seq)
-- WHERE is_validated = False
;
CREATE UNIQUE INDEX registrationnumber_key ON the_persons(registrationnumber)
WHERE is_validated = True
;
INSERT INTO the_persons(is_validated,registrationnumber,last_name, dob)VALUES
( True, 'OKAY001', 'Smith', '1988-02-02')
,( True, 'OKAY002', 'Jones', '1988-02-02')
,( False, 'OKAY001', 'Smith', '1988-02-02')
,( False, 'OMG001', 'Smith', '1988-08-02')
;
-- validated records:
SELECT *
FROM the_persons
WHERE is_validated = True
;
-- some records with nasty cousins
SELECT *
FROM the_persons p
WHERE EXISTS (
SELECT*
FROM the_persons x
WHERE x.registrationnumber = p.registrationnumber
AND x.is_validated = False
)
AND last_name LIKE 'Smith%'
;
你能提供样本数据吗?如果temp
是你唯一的非唯一重复值,我会选择partial
唯一索引
。你不能使用唯一的“temp”值,如“temp01”、“temp02”吗?使用创建日期作为标识符的一部分,可以最大限度地减少管理其中重复项的需要。这在很大程度上取决于ID的性质和确切要求。它会改变吗?是数字的吗?它是有意义的还是可以是普通的串行列?有多个表示“temp”的标志,并使它们唯一。例如,如果ID介于-1和-999之间,则它是一个临时标志。现在你仍然可以有一个唯一的索引,没有任何问题,除非你有一千个临时的。根据您的工作流程,您可能必须允许临时ID变异为永久ID,并级联到引用它们的所有外键。哇,太简单了。我还刚刚意识到,这基本上是教科书示例的应用,postgres示例提供了关于部分索引的相同警告…-><代码>索引列和谓词中使用的列不需要匹配。(…)请记住,谓词必须匹配查询中使用的条件,这些条件应该从索引中受益。
谢谢!(1) 该栏不起作用,因为该栏必须手动输入,但我未能在我的问题-althou中提供此信息