如何建模资源的所有权(例如信用卡)

时间:2016-08-03 12:48:08

标签: sql database-design

我正在试图弄清楚如何建模资源的所有权。一个例子可以是信用卡。在某种情况下,信用卡可以由companybusinessbusiness_admin拥有,用于支付属于business的服务。

我想出的是一个表格business_payment,其中引用了company_payment_methodbusiness_payment_methodbusiness_admin_payment_method以及获得指定付款方式的business 。它们都是可空的,需要触发器来检查插入是否有效 - 在插入之前,一个 ID必须不为null:

CREATE TABLE business_payment (

    business_id BIGINT NOT NULL,

    CONSTRAINT fk__business_payment__business
        FOREIGN KEY (business_id)
        REFERENCES business(id)
            ON DELETE CASCADE,

    company_payment_method_id BIGINT,

    CONSTRAINT fk__business_payment__company_payment_method
        FOREIGN KEY (company_payment_method_id)
        REFERENCES company_payment_method(id)
            ON DELETE CASCADE,

    business_payment_method_id BIGINT,

    CONSTRAINT fk__business_payment__business_payment_method
        FOREIGN KEY (business_payment_method_id)
        REFERENCES business_payment_method(id)
            ON DELETE CASCADE,

    business_admin_payment_method_id BIGINT,

    CONSTRAINT fk__business_payment__business_admin_payment_method
        FOREIGN KEY (business_admin_payment_method_id)
        REFERENCES business_admin_payment_method(id)
            ON DELETE CASCADE
);

我在这里看到的优点是,如果删除了任何信用卡,例如

DELETE FROM company_credit_card WHERE id = @companyCreditCardId;

business_payment_method也会被删除。在应用程序层,没有人必须负责清理它。

Con:

另一方面,我现在有一个具有NULL值的表格,如果有的话,有一个新实体x_credit_card我必须将此列添加到此表中,并确保触发器正常工作。

所以这是我正在考虑的解决方案,但我不确定这是否非常优雅 - 特别是因为我正在使用(M * N - M)NULL条目创建M * N矩阵。

我可以做得比这个设置好吗?

如果你想查看它,下面是完整的代码。不幸的是,由于DELIMITER这个问题,我不能让它在SQLFiddle上运行。

DROP TABLE IF EXISTS company_business;
DROP TABLE IF EXISTS company_employee;

DROP TABLE IF EXISTS payment_method;

DROP TABLE IF EXISTS business_payment;

DROP TABLE IF EXISTS business_admin_payment_method;
DROP TABLE IF EXISTS company_payment_method;
DROP TABLE IF EXISTS business_payment_method;

DROP TABLE IF EXISTS company;
DROP TABLE IF EXISTS business_admin;
DROP TABLE IF EXISTS business;

CREATE TABLE company (      
    id BIGINT AUTO_INCREMENT PRIMARY KEY
);

CREATE TABLE business_admin (       
    id BIGINT AUTO_INCREMENT PRIMARY KEY
);

CREATE TABLE business (
    id BIGINT AUTO_INCREMENT PRIMARY KEY
);

CREATE TABLE company_employee (

    company_id BIGINT NOT NULL,

    CONSTRAINT fk__company_employee__company 
        FOREIGN KEY (company_id)
        REFERENCES company(id)
            ON DELETE CASCADE,

    business_admin_id BIGINT NOT NULL,

    CONSTRAINT fk_company_employee__business_admin
        FOREIGN KEY (business_admin_id)
        REFERENCES business_admin(id)
            ON DELETE CASCADE,

    PRIMARY KEY (company_id, business_admin_id)

);

CREATE TABLE company_business (

    company_id BIGINT NOT NULL,

    CONSTRAINT fk__company_business__company
        FOREIGN KEY (company_id)
        REFERENCES company(id)
            ON DELETE CASCADE,

    business_id BIGINT NOT NULL,

    CONSTRAINT fk__company_business__business
        FOREIGN KEY (business_id)
        REFERENCES business(id)
            ON DELETE CASCADE,

    PRIMARY KEY (company_id, business_id)

);

SET @businessAdminId1 = 1;
INSERT INTO business_admin(id) VALUES (@businessAdminId1);

SET @companyId1 = 1;
INSERT INTO company(id) VALUES (@companyId1);
INSERT INTO company_employee(company_id, business_admin_id) VALUES (@companyId1,@businessAdminId1);

SET @businessId1 = 1;
INSERT INTO business VALUES (@businessId1);
INSERT INTO company_business VALUES(@companyId1, @businessId1);

CREATE TABLE company_payment_method (

    id BIGINT AUTO_INCREMENT PRIMARY KEY,

    company_id BIGINT NOT NULL,

    CONSTRAINT fk__company_payment_method__company
        FOREIGN KEY (company_id)
        REFERENCES company(id)
            ON DELETE CASCADE,

    payment_method_token VARCHAR(128) NOT NULL
);

CREATE TABLE business_payment_method (

    id BIGINT AUTO_INCREMENT PRIMARY KEY,

    business_id BIGINT NOT NULL,

    CONSTRAINT fk__business_payment_method__business
        FOREIGN KEY (business_id)
        REFERENCES business(id)
            ON DELETE CASCADE,

    payment_method_token VARCHAR(128) NOT NULL
);

CREATE TABLE business_admin_payment_method (

    id BIGINT AUTO_INCREMENT PRIMARY KEY,

    business_admin_id BIGINT NOT NULL,

    CONSTRAINT fk__business_admin_payment_method__business_admin
        FOREIGN KEY (business_admin_id)
        REFERENCES business_admin(id)
            ON DELETE CASCADE,

    payment_method_token VARCHAR(128) NOT NULL
);

CREATE TABLE business_payment (

    business_id BIGINT NOT NULL,

    CONSTRAINT fk__business_payment__business
        FOREIGN KEY (business_id)
        REFERENCES business(id)
            ON DELETE CASCADE,

    company_payment_method_id BIGINT,

    CONSTRAINT fk__business_payment__company_payment_method
        FOREIGN KEY (company_payment_method_id)
        REFERENCES company_payment_method(id)
            ON DELETE CASCADE,

    business_payment_method_id BIGINT,

    CONSTRAINT fk__business_payment__business_payment_method
        FOREIGN KEY (business_payment_method_id)
        REFERENCES business_payment_method(id)
            ON DELETE CASCADE,

    business_admin_payment_method_id BIGINT,

    CONSTRAINT fk__business_payment__business_admin_payment_method
        FOREIGN KEY (business_admin_payment_method_id)
        REFERENCES business_admin_payment_method(id)
            ON DELETE CASCADE
);

DELIMITER //

CREATE TRIGGER before_insert_business_payment_method BEFORE INSERT ON business_payment
FOR EACH ROW BEGIN
    DECLARE notNullForeignKeyFound BOOLEAN;
    DECLARE errorMessage VARCHAR(100);

    SET errorMessage = 'Exact one foreign key must be not null!';   
    SET notNullForeignKeyFound = FALSE;

    -- Company credit card ID

    IF NEW.company_payment_method_id IS NOT NULL THEN
        SET notNullForeignKeyFound = TRUE;
    END IF;

    -- Business credit card ID

    IF NEW.business_payment_method_id IS NOT NULL THEN
        IF notNullForeignKeyFound IS TRUE THEN
            SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = errorMessage;
        END IF;
        SET notNullForeignKeyFound = TRUE;
    END IF;

    -- Business admin credit card ID

    IF NEW.business_admin_payment_method_id IS NOT NULL THEN
        IF notNullForeignKeyFound IS TRUE THEN
            SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = errorMessage;
        END IF;
        SET notNullForeignKeyFound = TRUE;
    END IF;

    -- Check if at least one ID is not null

    IF notNullForeignKeyFound IS FALSE THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = errorMessage;
    END IF;
END//

DELIMITER ;

SET @companyCreditCardId1 = 1;
INSERT INTO company_payment_method (id, company_id, payment_method_token) VALUES (@companyCreditCardId1, @companyId1, 'wergef');

SET @businessCreditCardId1 = 1;
INSERT INTO business_payment_method (id, business_id, payment_method_token) VALUES (@businessCreditCardId1, @businessId1, 'asjio');

--
-- Here comes the actual action ..
-- 

-- Succeeds
INSERT INTO business_payment (business_id, business_payment_method_id) VALUES (@businessId1, @companyCreditCardId1);

-- Fails
-- INSERT INTO business_payment (business_id, company_payment_method_id, business_payment_method_id) VALUES (@businessId1, @companyCreditCardId1, @businessCreditCardId1);


-- The following will delete:
--   + business_payment_method.payment_method_id = 1
--   + busuiness_payment_method.payment_method_id = 1

DELETE FROM business_payment_method WHERE id = @companyCreditCardId1;

1 个答案:

答案 0 :(得分:1)

另一种方法是让business_entity表具有entity_type(业务,员工,管理员等)。付款可以将单个外键返回给实体。然后,您将获得每个实体类型的详细信息表,其中包含该特定实体类型的详细信息。

business_entity--1----M--business_payment | |--1-----1--employee |--1-------1--admin