SQL can I have a “conditionally unique” constraint on a table?

后端 未结 5 733
时光说笑
时光说笑 2020-12-17 18:03

I\'ve had this come up a couple times in my career, and none of my local peers seems to be able to answer it. Say I have a table that has a \"Description\" field which is a

相关标签:
5条回答
  • 2020-12-17 18:19

    If you are using SQL Server 2008 a Index filter would maybe your solution:

    http://msdn.microsoft.com/en-us/library/ms188783.aspx

    This is how I enforce a Unique Index with multiple NULL values

    CREATE UNIQUE INDEX [IDX_Blah] ON [tblBlah] ([MyCol]) WHERE [MyCol] IS NOT NULL
    
    0 讨论(0)
  • 2020-12-17 18:21

    In the case of descriptions which are not yet completed, I wouldn't have those in the same table as the finalized descriptions. The final table would then have a unique index or primary key on the description.

    In the case of the active/inactive, again I might have separate tables as you did with an "archive" or "history" table, but another possible way to do it in MS SQL Server at least is through the use of an indexed view:

    CREATE TABLE Test_Conditionally_Unique
    (
        my_id   INT NOT NULL,
        active  BIT NOT NULL DEFAULT 0
    )
    GO
    CREATE VIEW dbo.Test_Conditionally_Unique_View
    WITH SCHEMABINDING
    AS
        SELECT
            my_id
        FROM
            dbo.Test_Conditionally_Unique
        WHERE
            active = 1
    GO
    CREATE UNIQUE CLUSTERED INDEX IDX1 ON Test_Conditionally_Unique_View (my_id)
    GO
    
    INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
    VALUES (1, 0)
    INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
    VALUES (1, 0)
    INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
    VALUES (1, 0)
    INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
    VALUES (1, 1)
    INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
    VALUES (2, 0)
    INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
    VALUES (2, 1)
    INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
    VALUES (2, 1)    -- This insert will fail
    

    You could use this same method for the NULL/Valued descriptions as well.

    0 讨论(0)
  • 2020-12-17 18:24

    I'm not entirely aware of your intended use or your tables, but you could try using a one to one relationship. Split out this "sometimes" unique column into a new table, create the UNIQUE index on that column in the new table and FK back to the original table using the original tables PK. Only have a row in this new table when the "unique" data is supposed to exist.

    OLD tables:

    TableA
    ID    pk
    Col1  sometimes unique
    Col...
    

    NEW tables:

    TableA
    ID
    Col...
    
    TableB
    ID   PK, FK to TableA.ID
    Col1 unique index
    
    0 讨论(0)
  • 2020-12-17 18:29

    Thanks for the comments, the initial version of this answer was wrong.

    Here's a trick using a computed column that effectively allows a nullable unique constraint in SQL Server:

    create table NullAndUnique 
        (
        id int identity, 
        name varchar(50),
        uniqueName as case 
            when name is null then cast(id as varchar(51)) 
            else name + '_' end,
        unique(uniqueName)
        )
    
    insert into NullAndUnique default values
    insert into NullAndUnique default values -- Works
    insert into NullAndUnique default values -- not accidentally :)
    insert into NullAndUnique (name) values ('Joel')
    insert into NullAndUnique (name) values ('Joel') -- Boom!
    

    It basically uses the id when the name is null. The + '_' is to avoid cases where name might be numeric, like 1, which could collide with the id.

    0 讨论(0)
  • 2020-12-17 18:39

    Oracle does. A fully null key is not indexed by a Btree in index in Oracle, and Oracle uses Btree indexes to enforce unique constraints.

    Assuming one wished to version ID_COLUMN based on the ACTIVE_FLAG being set to 1:

    CREATE UNIQUE INDEX idx_versioning_id ON mytable 
      (CASE active_flag WHEN 0 THEN NULL ELSE active_flag END,
       CASE active_flag WHEN 0 THEN NULL ELSE id_column   END);
    
    0 讨论(0)
提交回复
热议问题