mysql - 带有空值的唯一索引 - 任何人的实际解决方案?

jur*_*iks 9 mysql null unique-constraint

CREATE TABLE IF NOT EXISTS b2c_constants (
    id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(64) NOT NULL,
    is_deleted BOOL DEFAULT FALSE,
    UNIQUE (name)
) ENGINE InnoDB CHARSET utf8 COLLATE utf8_unicode_ci

CREATE TABLE IF NOT EXISTS b2c_constant_bindings (
    constant_id INT UNSIGNED NOT NULL,
    company_id INT UNSIGNED NOT NULL,
    object_id INT UNSIGNED DEFAULT NULL,
    property_id INT UNSIGNED DEFAULT NULL,
    value VARCHAR(255) NOT NULL,
    UNIQUE (constant_id, company_id, object_id, property_id),
    FOREIGN KEY (constant_id) REFERENCES b2c_constants (id) ON UPDATE RESTRICT ON DELETE CASCADE,
    FOREIGN KEY (company_id) REFERENCES companies (id) ON UPDATE RESTRICT ON DELETE CASCADE,
    FOREIGN KEY (object_id) REFERENCES b2b_objects (id) ON UPDATE RESTRICT ON DELETE CASCADE,
    FOREIGN KEY (property_id) REFERENCES b2b_properties (id) ON UPDATE RESTRICT ON DELETE CASCADE
) ENGINE InnoDB CHARSET utf8 COLLATE utf8_unicode_ci
Run Code Online (Sandbox Code Playgroud)

问题在于bindings表中的唯一键。如果我有这样的数据:

constant_id   company_id   object_id   property_id   value
1             1            null        null          foo
1             1            1           null          bar
1             1            1           1             baz
Run Code Online (Sandbox Code Playgroud)

可以无限复制前两行而没有任何错误,这显然是不可取的。

这种结构的想法是允许全局绑定每个公司、公司中的每个对象和公司中的每个属性的常量,但只允许为这些中的每一个绑定唯一的常量。

有没有办法在数据库中解决这个问题,同时保持外键和简单的表结构?我知道我可以做type ENUM ('company', 'object', 'property'), type_id INT,但是我丢失了外键以及所有常量所需的公司 ID。

Jeh*_*aki 12

[这没有经过测试,因为它需要 MySQL 5.7.6 或更高版本]

虽然我同意上面的评论,但我仍然有一个可以尝试的想法,我认为这在性能方面不是最好的,但它解决了您所描述的问题。

这个想法是添加一个NULL作为具体值处理的键,如“0”或任何其他值。然后,对要唯一的字段组合进行唯一索引。

MySQL 5.7.6 支持生成列

ALTER TABLE b2c_constant_bindings
ADD unique_md5 char(32) AS 
    (MD5(CONCAT_WS('X', ifnull(constant_id, 0), ifnull(company_id, 0), ifnull(object_id, 0), ifnull(property_id,0)))) 
    UNIQUE;
Run Code Online (Sandbox Code Playgroud)

CONCAT我没有使用CONCAT_WS,而是用来避免在类似于CONCAT(1, 23)和的情况下出现相同的结果CONCAT(12, 3)(根据@Rick James 的通知,ss)。