web-dev-qa-db-ja.com

mysql-nullを含む一意のインデックス-実際のソリューションは誰か?

_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
_

問題は、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
_

最初の2つの行をエラーなしで無限に複製できますが、これは明らかに望ましくありません。

この構造のアイデアは、会社ごと、会社内のオブジェクトごと、会社内のプロパティごとにグローバルに定数をバインドできるようにするが、それぞれに一意の定数のみを許可することです。

外部キーと単純なテーブル構造を維持しながら、データベースでこれを解決する方法はありますか?私はtype ENUM ('company', 'object', 'property'), type_id INTを実行できることを知っていますが、それによって外部キーとすべての定数に必要な会社IDが失われます。

7
jurchiks

[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;
_

CONCATを使用する代わりに、CONCAT(1, 23)CONCAT(12, 3)に似たケースで同じ結果になるのを避けるために_CONCAT_WS_を使用しています(@Rick Jamesの通知ごとのss )。

8
Jehad Keriaki