Mysql unique constraint allowing single row for a combination

前端 未结 5 1226
囚心锁ツ
囚心锁ツ 2021-01-20 01:25

Is it possible to have a unique constraint such that one particular column has a value only once?

For instance

-----------------------    
name | pri         


        
相关标签:
5条回答
  • 2021-01-20 01:53

    A normal way to do this is to extract a separate table to hold the default price :

    CREATE TABLE price (
        name VARCHAR(255),
        price INT,
        PRIMARY KEY (name, price)
    ) ;
    
    CREATE TABLE defaultPrice (
        name VARCHAR(255),
        price INT,
        PRIMARY KEY (name),
        FOREIGN KEY(name, price) REFERENCES price(name, price)
    );
    

    Most people will advise introducing surrogate keys:

    CREATE TABLE item (
        id INT PRIMARY KEY,
        name VARCHAR(255),
        UNIQUE(name)
    );
    
    CREATE TABLE price (
        itemId INT,
        price INT,
        PRIMARY KEY (itemId, price),
        FOREIGN KEY (itemId) REFERENCES item (id)
    ) ;
    
    CREATE TABLE defaultPrice (
        itemId INT,
        price INT,
        PRIMARY KEY (itemId),
        FOREIGN KEY (itemId, price) REFERENCES price (itemId, price)
    );
    
    0 讨论(0)
  • 2021-01-20 02:01

    I have solved this problem with a strange aproch, using TIMESTAMP as a flag.

    That`s usead as a "deleted" FLAG.

    If IS NULL, then the register it's not deleted - Instead, if is deleted (<> NULL), the unique key will never conflict avoiding duplicate registers for non flaged as deleted registers.

    For example in your case:

    You will never have 2 rows with default: NULL, but you can have N with TIMESTAMP with they exclude time.

    0 讨论(0)
  • 2021-01-20 02:03

    You could make a trigger that checks if there allready is a field with the 'TRUE' value, and if so take action.

    Note that you cannot easily "reject" the update. (see e.g. : How to abort INSERT operation in MySql trigger? ).

    You could for instance just insert it with false, and save your error somehow, by setting a flag.

    0 讨论(0)
  • 2021-01-20 02:04

    Putting a simple unique constraint over all three columns would not work because it would allow the following:

    name | price | default
    -----------------------
    XYZ  |  20   | TRUE
    XYZ  |  30   | TRUE
    XYZ  |  40   | FALSE
    ABC  |  50   | FALSE
    

    In other SQL engines, you could just create a check constraint that ensures that for every set of name rows, the number of those rows with default = TRUE is <= 1. However, MySQL does not support enforcing check constraints.

    In MySQL you could implement this via insert and update triggers instead:

    CREATE TRIGGER `before_insert` BEFORE INSERT ON `tableName`
    FOR EACH ROW
    BEGIN
    
       DECLARE @sum INT
    
       SELECT @sum = SUM(CASE WHEN [default] = TRUE THEN 1 ELSE 0 END)
       FROM tableName
       WHERE [name] = new.[name]
    
       IF (@sum = 0 OR new.default = FALSE)
       BEGIN
          INSERT INTO tableName (name, price, default)
          VALUES (new.[name], new.[price], new.[defaul]t)
    
       END
    END
    

    And similarly for the update.

    0 讨论(0)
  • 2021-01-20 02:09

    Here is a workaround which might suit your needs. Consider the following table definition and unique constraint:

    CREATE TABLE prices
    (
        id int NOT NULL AUTO_INCREMENT,
        name varchar(255) NOT NULL,
        price int,
        default varchar(255)
    )
    
    ALTER TABLE prices ADD UNIQUE idx (id, name, default);
    

    In your app layer, when you INSERT records when default is FALSE, always leave the id column NULL. This will cause MySQL to always add a unique value for the id, so that duplicate values for name and default may be entered (i.e. a default value of FALSE may occur multiple times for a given name).

    However, when you INSERT a record with default being TRUE, you should always use the same id value. This will ensure that a given name can only appear once as TRUE.

    0 讨论(0)
提交回复
热议问题