これは、invoice_items に関するいくつかの小さな設計上の問題です。
ID (PK, autoincrement)
invoiceid (FK to invoices)
memberid (FK to members)
planid (FK to plans)
price (decimal)
ID は不要であり、他に変更がなければ、壊れた代理キーです。サロゲートとして自然キーの代わりになるはずですが、自然キーを作成していないため、壊れています。(代理母という言葉は、本質的に「代理母」を意味します。たとえば、代理母は、自然分娩の母親の代わりになります。)
いくつかの疑似 SQL を見てみましょう。
create table invoice_items (
invoiceid integer not null,
-- Traditionally, line items are numbered sequentially starting at 1
-- for each invoice. Your traditions might be different. "Some sane value"
-- prevents you from sending someone a 10,000 line invoice by mistake.
-- See below (way below) for implementing CHECK() constraints in MySQL.
-- A constraint that says, "Start with 1, no gaps" would be nice. I'll
-- let you code that one. ;)
line_item_num integer not null check (line_item_num >= 1 and
line_item_num <= [some sane value]),
memberid integer not null,
planid integer not null,
-- Choose the range to fit your application, and to prevent egregious mistakes.
price decimal(...) not null check (price between -10000 and 10000),
-- This constraint implements the traditional idea of invoice line items.
primary key (invoiceid, line_item_num),
-- This constraint prevents billing a single member plan twice on one invoice.
-- It might need to be dropped. For example, if you invoice one line item for
-- the base price for a member plan, then also invoice one line item for
-- a discount to the same member plan, you'd need to drop this constraint.
unique (invoiceid, memberid, planid),
foreign key (invoiceid) references invoices (invoiceid),
-- This foreign key needs to reference the single table member_plans, not the
-- two tables members and plans. Referencing the single table prevents you from
-- invoicing a member for a plan that member hasn't chosen.
foreign key (memberid, planid) references member_plans (memberid, planid)
);
この表の一部として「説明」に言及しましたが、列のリストから除外しました。私も捨てました。
MySQL の制約を確認する
MySQL は CHECK() 制約をサポートしていません。場合によっては、別のテーブルへの外部キー参照として CHECK() 制約を実装することが実用的です。たとえば、上記の line_item_num に対して記述した CHECK() 制約を、行項目番号のテーブルへの外部キー参照として実装すると実用的です。
また、外部キー参照が実用的でない場合もあります。たとえば、価格の範囲が広すぎて、この方法を実装できない場合があります。-10000.00 から +10000.00 の範囲には、数百万行かかります。1 つの代替方法は、トリガーを使用することです。最悪の場合、アプリケーション コードと例外レポートに頼らざるを得なくなる可能性があります。(クラックをすり抜けた無効な値を検索するために、例外レポートが時折実行されます。)
他のいくつかのことを見てください。. .
通常、請求書 ID 番号は自動インクリメント整数ではありません。自動インクリメント整数にはギャップがある場合があります。会計士はギャップを嫌います。遅かれ早かれ、彼らは請求書番号 10156 に何が起こったのかを知りたいと思うようになるでしょう。
employees.empID が一意である場合、ID 番号を持つ別の列を追加しても一意性は高くなりません。members.memberid についても同様です。カーゴ カルト プログラミングを参照してください。