sql - 每个客户有选择地覆盖主数据的数据库结构

标签 sql database data-modeling

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

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

数据模型要求:

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

在这个例子中:

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

下面是一个满足所有要求的注释示例实现。

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

制造商表

/*
 * 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

/*
 * 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

最佳答案

您需要以下表格:

  • 制造商代码
  • 制造商类型代码
  • 制造商详细信息
  • 型号代码
  • 模型类型代码
  • 模型细节

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

回复:制造商和客户 目前,您需要 PK 为 MANUFACTURER-ID 和 CUSTOMER-ID。最好将制造商拆分为制造商代码和制造商详细信息。 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)
  • DESCRIPTION 不为空

制造商类型代码 |描述
硕士 |大师
自定义 |自定义

制造商详细信息

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

制造商详细信息 ID |制造商代码 |制造商类型代码 |客户编号
1 |宝马 |硕士 | 1
2 |宝马 |自定义 | 1

型号

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

型号编号 |制造商详细信息 ID |描述
1 | 1 | M3
1 | 2 | M3降低

关于sql - 每个客户有选择地覆盖主数据的数据库结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1146092/

相关文章:

使用 ActiveRecord 进行 MySQL 分区

c++ - 通过条目维护/更新/访问文件的“良好”编程形式

数据库与许多表的关系以及 Entity Framework 中的表示

SQL 连接主键上的行值

php - 如何分别从 mysql 数据库回显单个字符串值?

sql - mysql 查询 : show summary of all payments made and total of invoices by date

sql - 如何在没有超时的情况下锁定 Ibm Db2 中的更新行?

postgresql - 在 Postgres 中存储固定长度小数组的最佳数据模型(使用 SqlAlchemy)

redis - 如何在Redis中相交并找到不同的键

sql - 如何在 SQL 中使用来自另一个数据库的用户定义函数