Нормализация для сокращения блокировок
Erik Darling. Normalizing To Reduce Blocking
Посыл
Я вижу множество таблиц, которые выглядят примерно так:
CREATE TABLE dbo.orders
(
order_id int NOT NULL PRIMARY KEY
DEFAULT (NEXT VALUE FOR dbo.order_id),
order_date datetime NOT NULL,
order_ship_date datetime NOT NULL,
order_total money NOT NULL,
order_tax money NOT NULL,
customer_id int NOT NULL
DEFAULT (NEXT VALUE FOR dbo.customer_id),
customer_fullname nvarchar(250),
customer_street nvarchar(250),
customer_street_2 nvarchar(250),
customer_city nvarchar(250),
customer_state nvarchar(250),
customer_zip nvarchar(250),
customer_country nvarchar(250)
);
Глядя на структуру, очевидны две большие проблемы:
Чем больше связанных, но не независимых данных вы храните в одной таблице, тем сложнее эффективно индексировать эту таблицу.
Если следовать некоторым практическим рекомендациям и разнести столбцы с соответствующими именами в собственные таблицы, мы получим:
Это лучший сценарий, поскольку мы сохраняем информацию о заказчике только один раз, а вставки/обновления информации о заказах не оказывают влияние на людей, работающих с данными заказчиков.
Но пока не идеально - что если заказчик пожелает отправить заказ по другому адресу?
Если бы мы захотели хранить все в этой таблице, то нарушили бы другие практические правила: мы получили бы множество строк на одного пользователя, или же нам пришлось добавлять столбцы в таблицу, соответствующие разным адресам. В итоге будут перемешаны люди, которые не используют все эти дополнительные столбцы с теми, кто может иметь полдюжины адресов, на которые они отправляют заказы.
Лучше сформулировать таблицу клиентов следующим образом:
Чаще люди собираются отправлять вещи на один адрес - назовите его домом, если хотите. Возможно, это так же их платежный адрес, так что имеет смысл сделать его адресом по умолчанию и первым при выборе.
Тогда мы имеем таблицу EAV-данных (модель сущность-атрибут-значение), которая выглядит следующим образом:
В подобной таблице, куда бы ни отправлял заказчик по недефолтному адресу, он хранится здесь. Теперь заказчики могут иметь столько адресов, сколько хотят, не заставляя нас иметь избыточно раздутую таблицу с информацией по умолчанию и дополнительной.
При таком способе моделирования данных, нам нет необходимости иметь дополнительные столбцы для хранения множества адресов. Мы просто добавляем строки, и поскольку эти данные вряд ли будут обновляться, шаблон вставки/выборки должен иметь минимальную блокировку.
Я знаю, это ужасно. Возможно, вам придется написать соединения. Несчастный девелопер.
Конечно, это имеет больше смысла, если вы имеете дело с OLTP-нагрузкой. И, конечно же, многие из этих столбцов, вероятно, не должны быть такими длинными, но это тема совершенно другой статьи.
Когда вы имеете дело с данными для отчетов, обычно предпочтительна денормализация. Хотя, если вы составляете серьезную отчетность и используете индексы поколоночного хранения, я, вероятно, хотел бы сохранить строки максимально длинными и просто обращаться к ним по ключу в других таблицах. Фух.
Говорил я когда-нибудь, что символьные данные в базах данных были ошибкой?
- Столбцы заказа (order) будут принимать большое число вставок и обновлений.
- Одна и та же информация о заказчике будет сохраняться снова и снова.
Чем больше связанных, но не независимых данных вы храните в одной таблице, тем сложнее эффективно индексировать эту таблицу.
Лучше, но не идеально
Если следовать некоторым практическим рекомендациям и разнести столбцы с соответствующими именами в собственные таблицы, мы получим:
CREATE TABLE dbo.orders
(
order_id int NOT NULL PRIMARY KEY
DEFAULT (NEXT VALUE FOR dbo.order_id),
order_date datetime NOT NULL,
order_ship_date datetime NOT NULL,
order_total money NOT NULL,
order_tax money NOT NULL,
customer_id int NOT NULL
);
CREATE TABLE dbo.customers
(
customer_id int NOT NULL PRIMARY KEY
DEFAULT (NEXT VALUE FOR dbo.customer_id),
customer_fullname nvarchar(250),
customer_street nvarchar(250),
customer_street_2 nvarchar(250),
customer_city nvarchar(250),
customer_state nvarchar(250),
customer_zip nvarchar(250),
customer_country nvarchar(250)
);
Это лучший сценарий, поскольку мы сохраняем информацию о заказчике только один раз, а вставки/обновления информации о заказах не оказывают влияние на людей, работающих с данными заказчиков.
Но пока не идеально - что если заказчик пожелает отправить заказ по другому адресу?
Если бы мы захотели хранить все в этой таблице, то нарушили бы другие практические правила: мы получили бы множество строк на одного пользователя, или же нам пришлось добавлять столбцы в таблицу, соответствующие разным адресам. В итоге будут перемешаны люди, которые не используют все эти дополнительные столбцы с теми, кто может иметь полдюжины адресов, на которые они отправляют заказы.
Лучше сформулировать таблицу клиентов следующим образом:
CREATE TABLE dbo.customers
(
customer_id int NOT NULL PRIMARY KEY
DEFAULT (NEXT VALUE FOR dbo.customer_id),
default_fullname nvarchar(250),
default_street nvarchar(250),
default_street_2 nvarchar(250),
default_city nvarchar(250),
default_state nvarchar(250),
default_zip nvarchar(250),
default_country nvarchar(250)
);
Чаще люди собираются отправлять вещи на один адрес - назовите его домом, если хотите. Возможно, это так же их платежный адрес, так что имеет смысл сделать его адресом по умолчанию и первым при выборе.
Тогда мы имеем таблицу EAV-данных (модель сущность-атрибут-значение), которая выглядит следующим образом:
CREATE TABLE dbo.customers_address_book
(
address_id int NOT NULL
DEFAULT (NEXT VALUE FOR dbo.address_id),
customer_id int NOT NULL,
address_type tinyint,
customer_fullname nvarchar(250),
customer_street nvarchar(250),
customer_street_2 nvarchar(250),
customer_city nvarchar(250),
customer_state nvarchar(250),
customer_zip nvarchar(250),
customer_country nvarchar(250),
CONSTRAINT pk_cab_id PRIMARY KEY (customer_id, address_id)
);
В подобной таблице, куда бы ни отправлял заказчик по недефолтному адресу, он хранится здесь. Теперь заказчики могут иметь столько адресов, сколько хотят, не заставляя нас иметь избыточно раздутую таблицу с информацией по умолчанию и дополнительной.
При таком способе моделирования данных, нам нет необходимости иметь дополнительные столбцы для хранения множества адресов. Мы просто добавляем строки, и поскольку эти данные вряд ли будут обновляться, шаблон вставки/выборки должен иметь минимальную блокировку.
Приправа
Я знаю, это ужасно. Возможно, вам придется написать соединения. Несчастный девелопер.
Конечно, это имеет больше смысла, если вы имеете дело с OLTP-нагрузкой. И, конечно же, многие из этих столбцов, вероятно, не должны быть такими длинными, но это тема совершенно другой статьи.
Когда вы имеете дело с данными для отчетов, обычно предпочтительна денормализация. Хотя, если вы составляете серьезную отчетность и используете индексы поколоночного хранения, я, вероятно, хотел бы сохранить строки максимально длинными и просто обращаться к ним по ключу в других таблицах. Фух.
Говорил я когда-нибудь, что символьные данные в базах данных были ошибкой?
Обратные ссылки
Автор не разрешил комментировать эту запись
Комментарии
Показывать комментарии Как список | Древовидной структурой