Все три типа платежей имеют несколько общих черт. Все они имеют номер счета, сумму, временную метку, тип платежа и какой-то идентификатор транзакции. Все общие атрибуты входят в одну таблицу. (Некоторые из типов данных, намеренно наивным, потому что они зависят от условий применения, и я не знаю, ваше приложение.)
create table payment_types (
payment_type_code char(2) primary key,
payment_type varchar(8) not null unique
);
insert into payment_types values
('Ca', 'Cash'),('Cr', 'Credit'),('Ba', 'Bank');
create table payments (
transaction_id integer primary key,
account_code varchar(5) not null, -- references accounts, not shown
amount_usd numeric(5,2) not null,
payment_type_code char(2) not null references payment_types (payment_type_code),
transaction_timestamp timestamp not null default current_timestamp,
unique (transaction_id, payment_type_code)
);
Уникальное ограничение на {TRANSACTION_ID, payment_type_code} позволяет SQL использовать эту пару столбцы как цель ограничения внешнего ключа. Это имеет решающее значение для того, чтобы не допустить смешения строк из нескольких таблиц.
Каждый платеж имеет разные атрибуты, в зависимости от типа оплаты. И каждый платеж может быть только одного типа.
create table payment_cash (
transaction_id integer primary key,
payment_type_code char(2) not null default 'Ca' check (payment_type_code = 'Ca'),
foreign key (transaction_id, payment_type_code)
references payments (transaction_id, payment_type_code),
other_cash_columns char(1) not null
);
create table payment_credit (
transaction_id integer primary key,
payment_type_code char(2) not null default 'Cr' check (payment_type_code = 'Cr'),
foreign key (transaction_id, payment_type_code)
references payments (transaction_id, payment_type_code),
other_credit_columns char(1) not null
);
create table payment_bank (
transaction_id integer primary key,
payment_type_code char(2) not null default 'Ba' check (payment_type_code = 'Ba'),
foreign key (transaction_id, payment_type_code)
references payments (transaction_id, payment_type_code),
other_bank_columns char(1) not null
);
Значение по умолчанию и проверить ограничение для payment_type_code делает это невозможным, например, вставить кредитные данные для оплаты наличными. То, что будет быть возможным - и это было бы Bad Thing - если ограничение внешнего ключа использовало только идентификатор транзакции.
Как правило, вы не каскадируете обновления или удаления для финансовых транзакций. Вместо этого исправьте ошибки, вставив компенсационную транзакцию.
Чтобы сделать это более удобным для пользователей и кода приложения, создайте три обновляемых представления, которые присоединяют таблицу платежей к деталям. Как сделать их обновляемыми, зависит от ваших dbms.
create view credit_payments_all as
select p.transaction_id, p.account_code, p.amount_usd,
p.payment_type_code, p.transaction_timestamp,
c.other_credit_columns
from payments p
inner join payment_credit c on c.transaction_id = p.transaction_id
-- Rules, triggers, stored procedures, functions, or whatever you need
-- to make this view updatable.
Тогда любой код, который должен вставить кредитную транзакцию, можно просто вставить в представление credit_payments_all.
Виды платежей не имеют разных деталей. * Платежи * имеют разные детали. –