MySQL: Первичный ключ банковской карты
Возникла интересная задача, которая не отпускает меня уже около недели. Допустим, что мы пишем приложение, в котором работаем с банковскими картами. Номер карты, как известно, состоит из 16 цифр. Знаю, что существуют номера и с другим количеством цифр, но в данном вопросе это не столь важно. Номер карты уникален и отлично подходит на роль первичного ключа, являясь естественным. В первом приближении всё хорошо. Проблемы начинаются дальше. Дело в том, что карта имеет ряд атрибутов. Основные атрибуты карты, а именно её лимиты, категория, платежная система и тип определяются БИН'ом. БИН - банковский идентификационный номер. Это первые 6 цифр номера карты. Каждая карта с одинаковым БИНом имеет одинаковые указанные атрибуты. Поэтому имеет смысл не дублировать информацию, а вынести атрибуты в отдельную таблицу. Первичным ключом же можно сделать тот самый БИН. А теперь самое интересное. Так как атрибуты вынесены из таблицы карт в таблицу БИНов, то каждая карта должна ссылаться на соответствующий бин. То есть каждая карта помимо БИНа в номере карты будет иметь еще и отдельное поле с БИНом, как с внешним ключом. То есть будет дублирование номера БИН. Не хорошо. Решение приходит само собой. Номер карты же составное число! Поэтому можно разбить номер. БИН будет отдельным полем и внешним ключом. Оставшаяся же часть номера из 12 цифр (вообще 11, но не суть) - это идентификатор карты. Но вспоминаем, что номер из 16 цифр был первичным ключом. А тут мы его разбиваем.. И вроде проблемы нет. 12 цифр же тоже можно сделать первичными. Да беда в том, что эти 12 цифр уже могут не быть уникальными. Что тогда использовать в качестве первичного ключа? Не вводить же id?
Ответы (1 шт):
Первый вариант - вычисляемое поле и его индексация. Схематично:
CREATE TABLE cards ( card_id BIGINT UNSIGNED NOT NULL CHECK (card_id BETWEEN 1000000000000000 AND 9999999999999999),
...
bin_code INT AS (card_id DIV 10000000000) VIRTUAL,
...
INDEX (bin_code),
...
);
Второй вариант - индекс по выражению. Схематично:
CREATE TABLE cards ( card_id BIGINT UNSIGNED NOT NULL CHECK (card_id BETWEEN 1000000000000000 AND 9999999999999999),
...
INDEX ((card_id DIV 10000000000)),
...
);