【问题标题】:Database structure for master data selectively overridden per customer每个客户有选择地覆盖主数据的数据库结构
【发布时间】:2010-11-11 21:04:37
【问题描述】:

对于这个问题,请考虑使用多租户数据库以及制造商和模型建模的应用程序。如果我们谈论汽车,那么制造商将是福特、雪佛兰、宝马等,而车型将是 F-150、Camaro 和 M3。

模型与制造商的关系是多对一的。每个租户的数据使用 customer_id 分隔。

对数据模型的要求:

  • 可以在主级别定义制造商和模型,以使所有客户都可以使用它们
  • 客户选择他们想要使用的主实体
  • 客户可以覆盖主模型或制造商的属性
  • 客户可以创建自己的制造商
  • 客户可以为自己或主要制造商创建自己的模型
  • 模型中的其他实体将与这些实体相关联,因此最好为每个实体提供一个主表,以便为其创建外键。制造商和型号表在示例中扮演了这个角色。

在这个例子中:

  • 客户 1 按原样使用福特,替代雪佛兰,并添加两个定制制造商
  • 客户 1 按原样使用雪佛兰和宝马,并添加了一家定制制造商
  • 根据脚本中的 cmets 创建模型

以下是符合所有要求的带注释的示例实现。

  • 如何改进?
  • 可以通过哪些其他方式对这些关系进行建模?

制造商表

/*
 * Master manufacturers shared between all customers
 */
CREATE TABLE master_manufacturers (
    master_manufacturer_id INTEGER NOT NULL,
    name VARCHAR(100) NOT NULL,
    attribute_1 VARCHAR(50),
    /* ... */
    attribute_n VARCHAR(50),
    PRIMARY KEY (master_manufacturer_id)
);

INSERT INTO
    master_manufacturers (master_manufacturer_id, name)
VALUES
    (1, 'Ford'),
    (2, 'Chevrolet'),
    (3, 'BMW');

/*
 * A Customer's manufacturer.  
 *   If master_manufacturer_id IS NULL, then it is a custom manufacturer and manufacturer_custom contains the data
 *   If master_manufacturer_id IS NOT NULL and manufacturer_custom does not exist, then the master is used without modification
 *   If master_manufacturer_id IS NOT NULL and manufacturer_custom exists, then the master is overridden
 */
CREATE TABLE manufacturers (
    manufacturer_id INTEGER NOT NULL,
    customer_id INTEGER NOT NULL,
    master_manufacturer_id INTEGER,
    PRIMARY KEY (manufacturer_id),
    FOREIGN KEY (master_manufacturer_id) REFERENCES master_manufacturers (master_manufacturer_id),
    UNIQUE (customer_id, master_manufacturer_id)
);

INSERT INTO
    manufacturers (manufacturer_id, customer_id, master_manufacturer_id)
VALUES
    (1, 1, 1),
    (2, 1, 2),
    (3, 1, NULL),
    (4, 1, NULL),
    (5, 2, 2),
    (6, 2, 3),    
    (7, 2, NULL);    

CREATE TABLE manufacturer_custom (
    manufacturer_id INTEGER NOT NULL,
    name VARCHAR(100) NOT NULL,
    attribute_1 VARCHAR(50),
    /* ... */
    attribute_n VARCHAR(50),
    PRIMARY KEY (manufacturer_id),
    FOREIGN KEY (manufacturer_id) REFERENCES manufacturers (manufacturer_id)
);

INSERT INTO
    manufacturer_custom (manufacturer_id, name)
VALUES
    (2, 'Chevy'),
    (3, 'Cust 1 Custom 1'),
    (4, 'Cust 1 Custom 2'),
    (7, 'Cust 2 Custom 1');

模型表

/*
 * Master models shared between all customers
 */
CREATE TABLE master_models (
    master_model_id INTEGER NOT NULL,
    master_manufacturer_id INTEGER NOT NULL,
    name VARCHAR(100) NOT NULL,
    attribute_1 VARCHAR(50),
    /* ... */
    attribute_n VARCHAR(50),
    PRIMARY KEY (master_model_id),
    FOREIGN KEY (master_manufacturer_id) REFERENCES master_manufacturers (master_manufacturer_id)
);

INSERT INTO
    master_models (master_model_id, master_manufacturer_id, name)
VALUES
    (1, 1, 'F-150'),
    (2, 1, 'F-250'),
    (3, 1, 'Falcon'),
    (4, 2, 'Camaro'),
    (5, 2, 'Corvette'),
    (6, 3, 'M3'),
    (7, 3, '135i');

/*
 * A Customer''s model.  
 *   If master_model_id IS NULL, then it is a custom model and model_custom contains the data
 *   If master_model_id IS NOT NULL and model_custom does not exist, then the master is used without modification
 *   If master_model_id IS NOT NULL and model_custom exists, then the master is overridden
 */
CREATE TABLE models (
    model_id INTEGER NOT NULL,
    master_model_id INTEGER,
    manufacturer_id INTEGER NOT NULL,
    attribute_1 VARCHAR(50),
    /* ... */
    attribute_n VARCHAR(50),
    PRIMARY KEY (model_id),
    FOREIGN KEY (master_model_id) REFERENCES master_models (master_model_id)
);

INSERT INTO
    models (model_id, master_model_id, manufacturer_id)
VALUES
    (1, 1, 1),    /* F-150 for customer_1's Ford */
    (2, 2, 1),    /* F-250 for customer_1's Ford */
    (3, 4, 2),    /* Camaro for customer_1's Chevy */
    (4, 4, 5),    /* Camaro for customer_2's Chevrolet */
    (5, 5, 5),    /* Corvette for customer_2's Chevrolet */
    (6, 6, 6),    /* M3 for customer_2's BMW */
    (7, NULL, 1), /* F-350 (custom) for customer_1's Ford */
    (8, NULL, 6), /* M7 (custom) for customer_2's BMW */
    (9, NULL, 7); /* Custom Model (custom) for customer_2's Custom Mfg */

CREATE TABLE model_custom (
    model_id INTEGER NOT NULL,
    name VARCHAR(100) NOT NULL,
    attribute_1 VARCHAR(50),
    /* ... */
    attribute_n VARCHAR(50),
    PRIMARY KEY (model_id),
    FOREIGN KEY (model_id) REFERENCES models (model_id)
);

INSERT INTO
    model_custom (model_id, name)
VALUES
    (7, 'F-350'),        /* F-350 for customer_1's Ford */
    (8, 'M7'),           /* M7 for customer_2's BMW */
    (9, 'Custom Model'); /* Custom Model for customer_2's Custom Mfg */

简化使用这些表格的视图

/*
 * View for a customer''s manufacturers
 */
CREATE VIEW vw_manufacturers AS
    SELECT
        m.customer_id,
        m.manufacturer_id, 
        COALESCE(cm.name, mm.name) AS name,
        COALESCE(cm.attribute_1, mm.attribute_1) AS attribute_1,
        /* ... */
        COALESCE(cm.attribute_n, mm.attribute_n) AS attribute_n
    FROM
        manufacturers m
    LEFT JOIN
        master_manufacturers mm
    USING
        (master_manufacturer_id)
    LEFT JOIN
        manufacturer_custom cm
    USING
        (manufacturer_id);

/*
 * View for a customer's models
 */
CREATE VIEW vw_models AS
    SELECT
        mfg.customer_id,
        mfg.manufacturer_id,
        mfg.name AS manufacturers_name,
        m.model_id,
        COALESCE(cm.name, mm.name) AS name,
        COALESCE(cm.attribute_1, mm.attribute_1) AS attribute_1,
        /* ... */
        COALESCE(cm.attribute_n, mm.attribute_n) AS attribute_n
    FROM
        vw_manufacturers mfg,
        models m
    LEFT JOIN
        master_models mm
    USING
        (master_model_id)
    LEFT JOIN
        model_custom cm
    USING
        (model_id)
    WHERE
        mfg.manufacturer_id = m.manufacturer_id;

customer_id 1 的制造商

SELECT manufacturer_id, name FROM vw_manufacturers WHERE customer_id = 1;

 manufacturer_id |      name       
-----------------+-----------------
           1 | Ford
           2 | Chevy
           3 | Cust 1 Custom 1
           4 | Cust 1 Custom 2

customer_id 2 的制造商

SELECT manufacturer_id, name FROM vw_manufacturers WHERE customer_id = 2;

 manufacturer_id |      name       
-----------------+-----------------
           5 | Chevrolet
           6 | BMW
           7 | Cust 2 Custom 1

customer_id 1 的模型

SELECT * FROM vw_models WHERE customer_id = 1;

 customer_id | manufacturer_id | manufacturers_name | model_id |  name  
-------------+-----------------+--------------------+----------+--------
       1 |               1 | Ford               |        1 | F-150
       1 |               1 | Ford               |        2 | F-250
       1 |               2 | Chevy              |        3 | Camaro
       1 |               1 | Ford               |        7 | F-350

customer_id 2 的模型

SELECT * FROM vw_models WHERE customer_id = 2;

 customer_id | manufacturer_id | manufacturers_name | model_id |     name     
-------------+-----------------+--------------------+----------+--------------
           2 |               5 | Chevrolet          |        4 | Camaro
           2 |               5 | Chevrolet          |        5 | Corvette
           2 |               6 | BMW                |        6 | M3
           2 |               6 | BMW                |        8 | M7
           2 |               7 | Cust 2 Custom 1    |        9 | Custom Model

【问题讨论】:

    标签: sql database data-modeling


    【解决方案1】:

    您需要以下表格:

    • 制造商代码
    • 制造商类型代码
    • 制造商详细信息
    • 型号代码
    • 型号-类型-代码
    • 型号详情

    如果您有具有相同数据的表 - 您需要合并它们,并使用 TYPE_CODE 表来区分它们。

    回复:制造商和客户 目前,您需要 PK 为 MANUFACTURER-ID 和 CUSTOMER-ID。最好将 MANUFACTURERS 拆分为 MANUFACTURERS-CODE 和 MANUFACTURER-DETAILS。 MANUFACTURER-CODE 将包含“BMW”、“FORD”等以及自定义。 MANUFACTURER-DETAILS 将允许您保留每个客户的详细数据,同时允许您重用诸如“BMW”/等之类的代码。模型也是如此。

    下一步是为引擎、车轮等定义 TYPE-CODE 表。我将使用名为 MODEL-ATTRIBUTES 的 XREF 表将这些与 MODEL-DETAILS 相关联。 MODEL-ATTRIBUTES 表将包含:

    • 型号-详细信息-ID (pk)
    • 型号-属性-类型-代码 (pk)
    • 属性代码 (pk)

    这将允许可选模型属性与适用的 MODEL-DETAILS 记录相关联,而无需不断向 MODEL-DETAILS 表添加属性。

    制造商代码

    • 制造商代码 VARCHAR(4) (pk)
    • 描述
    • 有效日期不为空
    • EXPIRY-DATE 不为空

    制造商代码 |说明 |生效日期 |到期日
    福特 |福特 | 01-01-1900 | 12-31-9999
    宝马 |宝马 | 01-01-1900 | 12-31-9999
    雪佛龙 |雪佛兰 | 01-01-1900 | 12-31-9999

    制造商类型代码

    • 制造商类型代码 (pk)
    • 描述不为空

    制造商类型代码 |说明
    大师 |大师
    定制 |自定义

    制造商详细信息

    • 制造商详细信息-ID (pk)
    • 制造商代码 (fk) 不为空
    • 制造商类型代码 (fk) 不为空
    • 客户 ID (fk) 不为空

    制造商详细信息-ID |制造商代码 |制造商类型代码 |客户编号
    1 |宝马 |大师 | 1
    2 |宝马 |定制 | 1

    型号

    • 型号 ID (pk)
    • MANUFACTURER-DETAILS-ID (fk) 不为空
    • 描述不为空

    型号 ID |制造商详细信息 ID |说明
    1 | 1 | M3
    1 | 2 | M3降低

    【讨论】:

    • 你能解释一下这是如何工作的吗?我不太明白。制造商结构的完整示例会很棒。试图解决它,我得到以下信息,但我认为这不是你在说的。 MANUFACTURER-CODE (mfg_id (PK), code) MANUFACTURER-DETAILS (mfg_id, (FK, PK), customer_id (PK), attr1, attr) MANUFACTURERS-TYPE-CODE (mfg_id (FK, PK), is_master)
    • customer_id 如何作为 MANUFACTURER-DETAILS 的非空列工作?在示例中,您将 MASTER 类型设置为 1,但 MASTER 类型应该可供所有客户使用。
    • Not null 表示该列不能有空值,这意味着在这种情况下,如果没有关联的 CUSTOMER-ID,您就不能在 MANUFACTURER-DETAILS 中拥有记录。 MASTER 值是来自 MANUFACTURER-TYPE-CODE 表的外键(因此是 fk 缩写) - 它是自然键,不使用数字来标识它。也就是说,当前数据模型没有限制哪些客户可以看到 MANUFACTURER-TYPE-CODE 记录的内容。如果你想要这个,你需要在 MANUFACTURER-TYPE-CODE 和 CUSTOMER 之间添加一个表,使用任一表中的 pk 组合作为 pk。
    【解决方案2】:

    这取决于该系统的使用。 您可以针对 OLAP 和 OLTP 进行不同的设计。

    理论上这可能都在一张表中......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-16
      • 2011-08-29
      • 2011-07-15
      • 2016-01-09
      • 2016-10-06
      • 1970-01-01
      • 2011-02-17
      相关资源
      最近更新 更多