Sql 在列上强制唯一性但允许某些重复项的最佳做法?

Sql 在列上强制唯一性但允许某些重复项的最佳做法?,sql,postgresql,database-design,unique-index,Sql,Postgresql,Database Design,Unique Index,我想弄明白的是:应该有一个表来存储新客户管理系统的授权,每个授权都有其唯一的标识符。这个约束很容易转换为SQL,但不幸的是,由于官僚作风的缓慢,有时我们需要创建一个带有占位符ID(例如,“temp”)的条目,以便客户端能够开始接受服务 实施此条件唯一性约束的最佳实践是什么 以下是我有限的经验所能提供的: 使用Postgresql手册(->)中提到的部分索引。它还提到,当成功的测试很少而失败的测试很多时,这是一种特别有效的方法。在我们将要迁移的遗留数据库中,每月有130000行和大约5个临时授权

我想弄明白的是:应该有一个表来存储新客户管理系统的授权,每个授权都有其唯一的标识符。这个约束很容易转换为SQL,但不幸的是,由于官僚作风的缓慢,有时我们需要创建一个带有占位符ID(例如,“temp”)的条目,以便客户端能够开始接受服务

实施此条件唯一性约束的最佳实践是什么

以下是我有限的经验所能提供的:

  • 使用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中提供此信息