以下是我要弄清楚的内容:应该有一个表来存储我们的新客户端管理系统的授权,并且每个授权都有其唯一标识符。这个约束很容易转换为SQL,但不幸的是,由于官僚机构的缓慢,有时我们需要创建一个带有占位符ID的条目(例如," temp"),以便客户端能够开始服务。
强制执行此条件唯一性约束的最佳做法是什么?
我可以用我有限的经验来提出这些:
This is a particularly efficient approach when there are few successful tests and many unsuccessful ones
。在我们要迁移的旧数据库中,每月有130,000行和大约5个临时授权,但整个表每年仅增长约200行。这是正确的方法吗? (我也不确定"效率"在这种情况下意味着什么。)编辑:
对不起,我认为我对授权ID的描述有点模糊:它由状态部门提供,格式为NMED012345678
,并且是手动输入的。它是独一无二的,但有时仅在以后for unknown reasons提供。
答案 0 :(得分:5)
有一种简单,快速和安全的方式:
添加布尔列以标记临时条目,默认情况下为NULL,例如:
temp bool DEFAULT NULL CHECK (temp)
添加的检查约束不允许FALSE
,只能NULL
或TRUE
。默认NULL值的存储成本通常是......没有 - 除非行中没有其他NULL值。
列默认表示您通常不必处理该列。它默认为NULL(which is the default default anyway,我只是在这里明确)。您只需要明确标记少数例外。
然后创建一个部分唯一索引,如:
CREATE UNIQUE INDEX tbl_unique_id_uni ON tbl (unique_id) WHERE temp IS NULL;
只包含应该是唯一的行。索引大小根本没有增加。
请务必将谓词WHERE temp IS NULL
添加到应该使用唯一索引的查询中。
相关:
答案 1 :(得分:2)
您可以有多种可能性:
使 temp 标识符唯一;例如,如果它们是自动创建的(未手动输入 ),请创建它们:
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'
。
答案 2 :(得分:1)
评论时间有点长。
我想我会拥有一个具有唯一授权的授权表。授权可以有两种类型:“已批准”和“临时”。你可以用两列来处理这个问题。
但是,我可能会将授权ID作为序列列,其中“已批准”的ID是表中的字段。该表可能有一个独特的约束。您可以使用完整的unique
约束或带有过滤值的唯一约束(Postgres允许在唯一约束中使用多个NULL
值,但第二个更明确)。
您可以使用相同的过程进行临时授权 - 使用不同的列。大概你有一些机制来授权它们并存储批准日期,时间和人。
我不会使用两张桌子。授权在多个表之间传播似乎很容易引起混淆。代码中您希望查看谁拥有授权的任何地方都可能会误读数据。
答案 3 :(得分:1)
IMO不建议使用远程键作为主键的一部分。
\ t 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%'
;