Структура базы данных для основных данных, выборочно переопределенная для каждого клиента - PullRequest
5 голосов
/ 18 июля 2009

Для этого вопроса рассмотрим приложение, использующее мультитенантную базу данных с моделированием производителей и моделей. Если говорить об автомобилях, то производители - это Ford, Chevrolet, BMW и т. Д., А модели - F-150, Camaro и M3.

Отношение модели к производителю много к одному. Данные для каждого арендатора отделяются с помощью customer_id.

Требования к модели данных:

  • Производители и модели могут быть определены на уровне мастера, чтобы сделать их доступными для всех клиентов
  • Клиент выбирает, какие из основных сущностей они хотели бы использовать
  • Клиенты могут переопределять атрибуты мастер-модели или производителя
  • Клиенты могут создавать своих собственных производителей
  • Клиенты могут создавать свои собственные модели для своего или основного производителя
  • Другие сущности в модели будут связаны с этими сущностями, поэтому желательно, чтобы для каждой из них была создана одна главная таблица, для которой можно сделать внешний ключ. Таблицы производителей и моделей выполняют эту роль в примере.

В этом примере:

  • Клиент 1 использует Ford как есть, переопределяет Chevrolet и добавляет двух изготовителей на заказ
  • Клиент 1 использует Chevrolet и BMW как есть и добавляет одного изготовителя на заказ
  • Модели создаются согласно комментариям в скрипте

Ниже приведен аннотированный пример реализации, который отвечает всем требованиям.

  • Как это можно улучшить?
  • Какими еще способами можно смоделировать эти отношения?

Таблицы изготовителя

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

Ответы [ 2 ]

2 голосов
/ 18 июля 2009

Вам нужны следующие таблицы:

  • ИЗГОТОВЛЕНИЕ-CODE
  • ПРОИЗВОДИТЕЛИ-TYPE-CODE
  • ИЗГОТОВЛЕНИЕ-ПОДРОБНОСТИ
  • MODEL-CODE
  • MODELS-TYPE-CODE
  • MODEL-ПОДРОБНОСТИ

Если у вас есть таблицы с идентичными данными - вам необходимо объединить их и использовать таблицу TYPE_CODE для их дифференциации.

Re: ПРОИЗВОДИТЕЛИ И ЗАКАЗЧИКИ В настоящее время вам нужно, чтобы в качестве идентификатора производителя использовался идентификатор изготовителя и идентификатор клиента. Лучше разбить ПРОИЗВОДИТЕЛЕЙ на ПРОИЗВОДИТЕЛЬ-КОД и ПРОИЗВОДИТЕЛЬ-ДЕТАЛИ. КОД ПРОИЗВОДИТЕЛЯ будет содержать «BMW», «FORD» и т. Д. Плюс обычай. ИЗГОТОВЛЕНИЕ ПРОИЗВОДИТЕЛЕЙ позволит вам хранить подробные данные по каждому клиенту, а также повторно использовать коды для таких вещей, как «BMW» и т. Д. То же самое касается моделей.

Следующим шагом будет определение таблиц TYPE-CODE для таких вещей, как двигатель, колеса и т. Д. Я бы связал их с MODEL-DETAILS, используя таблицу XREF с именем MODEL-ATTRIBUTES. Таблица MODEL-ATTRIBUTES будет содержать:

  • MODEL-DETAILS-ID (pk)
  • МОДЕЛЬ-АТРИБУТ-ТИП-КОД (pk)
  • КОД АТРИБУТА (pk)

Это позволило бы связать необязательные атрибуты модели с соответствующей записью MODEL-DETAILS без постоянного добавления атрибутов в таблицу MODEL-DETAILS.

ИЗГОТОВЛЕНИЕ-CODE

  • VARCHAR с кодом производителя (4) (шт.)
  • EFFECTIVE-DATE не нуль
  • EXPIRY-DATE не null

КОД ПРОИЗВОДИТЕЛЯ | ОПИСАНИЕ | ЭФФЕКТИВНО-ДАТА | EXPIRY-ДАТА
FORD | FORD | 01-01-1900 | 12-31-9999
BMW | BMW | 01-01-1900 | 12-31-9999
CHEV | Шевроле | 01-01-1900 | 12-31-9999

ИЗГОТОВЛЕНИЕ-TYPE-CODE

  • КОД ТИПА ПРОИЗВОДИТЕЛЯ (pk)
  • ОПИСАНИЕ not null

КОД ПРОИЗВОДИТЕЛЯ-ТИПА | ОПИСАНИЕ
МАСТЕР | Master
ПОЛЬЗОВАТЕЛЬСКИЙ | Custom

ИЗГОТОВЛЕНИЕ-ПОДРОБНОСТИ

  • ИДЕНТИФИКАТОР-ИЗГОТОВИТЕЛЬ (pk)
  • КОД ПРОИЗВОДИТЕЛЯ (fk) не ноль
  • КОД ТИПА ПРОИЗВОДИТЕЛЯ (fk) не ноль
  • CUSTOMER-ID (fk) не ноль

ПРОИЗВОДИТЕЛЬ-ДЕТАЛИ-ID | КОД ПРОИЗВОДИТЕЛЯ | КОД ПРОИЗВОДИТЕЛЯ-ТИПА | КЛИЕНТ-ID
1 | BMW | МАСТЕР | 1
2 | BMW | ПОЛЬЗОВАТЕЛЬСКИЙ | 1

MODEL

  • ID модели (pk)
  • ПРОИЗВОДИТЕЛЬ-ДЕТАЛИ-ID (fk) не ноль
  • ОПИСАНИЕ not null

MODEL-ID | ПРОИЗВОДИТЕЛЬ-ДЕТАЛИ-ID | ОПИСАНИЕ
1 | 1 | M3
1 | 2 | М3 понижен

0 голосов
/ 18 августа 2009

Это зависит от использования этой системы. Вы бы разработали это по-другому для OLAP против OLTP.

Теоретически все это может быть в 1 таблице ....

...