Yos*_*nar 6 mysql database-design subtypes
我有以下要求:
每笔交易都有以下类型之一;借记、贷记、存款或取款。
借方或贷方交易必须有链接的发票记录,并且没有银行账户记录。
存款或取款交易必须有关联的银行账户记录且无发票记录。
目前我的基本设计是这样的:
我目前的解决方案是:
我概述的哪种方法是更好的解决方案?或者还有其他更好的方法来满足我的约束吗?
尽管很多可为 NULL 的列通常是糟糕设计的标志,但在某些情况下,拥有它们并使用数据库CONSTRAINTS
来确保保留 0-1 关系和业务规则是完全合法的。这将是您的解决方案#1。您的解决方案#2 不容易得到数据库的帮助,您最终可能会拥有一个事务,而在 和invoices
表中都没有相应的行bank_accounts
。
让我们暂时假设您没有使用MySQL 1(至少从版本 5.7 开始)。
\n\n假设您正在使用另一个实际执行检查的数据库2,使用如下所示的模式是有意义的,其中包含一列invoice_id
和一列,以及保证它们正确的bank_account_id
必要约束REFERENCE
正确表中的行(您称之为links),并CHECKS
确保正确的行出现,不对应的行不存在:
CREATE TYPE transaction_type AS ENUM\n (\'debit\', \'credit\', \'deposit\', \'withdraw\') ;\n-- Note: this could be a table with four values (and probably four ids) \n\nCREATE TABLE invoices\n(\n invoice_id integer /* serial */ PRIMARY KEY,\n other_data text\n) ;\n\nCREATE TABLE bank_accounts\n(\n bank_account_id integer /* serial */ PRIMARY KEY,\n name text,\n other_data text\n) ;\n\nCREATE TABLE transactions\n(\n transaction_id integer /* serial */ PRIMARY KEY, \n type transaction_type,\n nominal decimal(10, 2),\n invoice_id integer REFERENCES invoices(invoice_id) ON UPDATE CASCADE ON DELETE RESTRICT,\n bank_account_id integer REFERENCES bank_accounts(bank_account_id) ON UPDATE CASCADE ON DELETE RESTRICT,\n\n -- Constraints for your business rules\n CONSTRAINT chk_debit_and_credit_must_have_bank_account \n CHECK (case when type in (\'debit\',\'credit\') then \n bank_account_id IS NOT NULL \n else true end),\n CONSTRAINT chk_debit_and_credit_must_not_have_invoice \n CHECK(case when type in (\'debit\',\'credit\') then \n invoice_id IS NULL \n else true end),\n CONSTRAINT chk_deposit_and_withdraw_must_have_invoice \n CHECK(case when type in (\'deposit\',\'withdraw\') then \n invoice_id IS NOT NULL \n else true end),\n CONSTRAINT chk_deposit_and_withdraw_must_not_have_bank_account \n CHECK(case when type in (\'deposit\',\'withdraw\') then \n bank_account_id IS NULL \n else true end) \n) ;\n
Run Code Online (Sandbox Code Playgroud)\n\n考虑到这一点,以及以下数据......
\n\n-- Adding two invoices\nINSERT INTO invoices\n (invoice_id, other_data)\nVALUES\n (1, \'data for invoice 1\'),\n (2, \'data for invoice 2\')\n;\n\n-- Adding two bank accounts\nINSERT INTO bank_accounts\n (bank_account_id, other_data)\nVALUES\n (1000, \'Bank account 1000\'),\n (1001, \'Bank account 1001\')\n;\n
Run Code Online (Sandbox Code Playgroud)\n\n...你可以拥有一个合法的INSERT
-- Good credit and debit\nINSERT INTO transactions \n (transaction_id, type, nominal, invoice_id, bank_account_id)\nVALUES\n (2000, \'credit\', 1000.00, NULL, 1000),\n (2001, \'debit\', 900.00, NULL, 1000) ;\n
Run Code Online (Sandbox Code Playgroud)\n\n...以及一些非法的(被数据库拒绝)
\n\n-- Bad credit, it\'s got invoice\nINSERT INTO transactions \n (transaction_id, type, nominal, invoice_id, bank_account_id)\nVALUES\n (2002, \'credit\', 1000.00, 1, 1000) ;\n
Run Code Online (Sandbox Code Playgroud)\n\n\n错误: 关系“交易”的新行违反了检查约束“chk_debit_and_credit_must_not_have_invoice”\n详细信息: 失败行包含 (2002, Credit, 1000.00, 1, 1000)。\n\n\n
-- Bad credit, it\'s got not bank_account_id\nINSERT INTO transactions \n (transaction_id, type, nominal, invoice_id, bank_account_id)\nVALUES\n (2003, \'credit\', 1000.00, NULL, NULL) ;\n
Run Code Online (Sandbox Code Playgroud)\n\n\n错误: 关系“交易”的新行违反了检查约束“chk_debit_and_credit_must_have_bank_account”\n详细信息: 失败行包含 (2003, Credit, 1000.00, null, null)。\n\n\n
(以及所有其他组合)
\n\n仔细选择约束的名称对于调试错误的插入或更新有很大帮助。如果您需要最大速度,所有约束都可以减少到只有一个检查表达式。我通常会尝试让数据库帮助我,并保持简单(4 个简单的名称和 4 个易于阅读的表达式,而不是单个)。
\n\n您可以在dbfiddle找到所有设置
\n\n1原因如下:
\n\n不幸的是,MySQL 没有多大帮助,因为来自MySQL 5.7 手册中关于 CREATE TABLE 的内容
\n\n\n\n\n查看
\n\nCHECK 子句被所有存储引擎解析但忽略。请参见第 1.8.2.3 节,\xe2\x80\x9c 外键差异\xe2\x80\x9d。
\n
2我使用过 PostgreSQL。通过一些语法变化,这也适用于 SQL Server 或 Oracle
\n