Колонка плюсов и минусов даты как часть первичного ключа - PullRequest
0 голосов
/ 26 апреля 2018

В настоящее время я работаю над базой данных, где требуется журнал для отслеживания множества различных изменений данных. Такие вещи, как изменения цен, изменения статуса проекта и т. Д. Для этого я создал различные таблицы «журналов», в которых будут храниться данные, которые необходимо сохранить.

Чтобы привести хороший пример, чтобы отслеживать изменяющиеся цены на детали, которые необходимо заказать, я создал таблицу под названием Part_Price_Log. Первичный ключ является составным и состоит из даты изменения цены детали и внешнего ключа для уникального идентификатора детали в таблице Parts.

Моя логика заключается в том, что если вам нужно посмотреть текущую цену на деталь, вам просто нужно найти самую последнюю запись для этого идентификатора детали. Однако мне говорят, что не следует реализовывать это таким образом, потому что использование Date как части первичного ключа - это простой способ получить ошибки в ваших данных.

Итак, мой вопрос таков.

Каковы плюсы / минусы использования столбца Date как части составного первичного ключа? Какие есть лучшие альтернативы?

Ответы [ 4 ]

0 голосов
/ 26 апреля 2018

Плюсы и минусы будут различаться в зависимости от требований к производительности и того, как часто вы будете запрашивать эту таблицу.

В качестве первого примера подумайте о следующем:

CREATE TABLE Part_Price_Log (
    ModifiedDate DATE,
    PartID INT,
    PRIMARY KEY (ModifiedDate, PartID))

Если ModifiedDate является первым и это таблица журналирования со строками только для вставки, то каждая новая строка будет помещена в конце, что хорошо (уменьшает фрагментацию). Этот подход также хорош, когда вы хотите фильтровать напрямую по ModifiedDate или ModifiedDate + PartID, так как ModifiedDate является первым столбцом в первичном ключе. Кон здесь будет искать по PartID, так как кластерный индекс первичного ключа не сможет напрямую искать PartID.

Второй пример будет таким же, но с инвертированным порядком первичного ключа:

CREATE TABLE Part_Price_Log (
    ModifiedDate DATE,
    PartID INT,
    PRIMARY KEY (PartID, ModifiedDate))

Это хорошо для запросов по PartID, но не очень для запросов напрямую по ModifiedDate. Кроме того, наличие PartID первым приведет к тому, что вставки смещают страницы данных, так как вставка PartID меньше максимальной PartID (что увеличивает фрагментацию).

В последнем примере будет использоваться суррогатный первичный ключ, такой как IDENTITY.

CREATE TABLE Part_Price_Log (
    LogID BIGINT IDENTITY PRIMARY KEY,
    ModifiedDate DATE,
    PartID INT)

Это сделает все вставки последними и уменьшит фрагментацию, но вам потребуется дополнительный индекс для запроса ваших данных, например:

CREATE NONCLUSTERED INDEX NCI_Part_Price_Log_Date_PartID ON Part_Price_Log (ModifiedDate, PartID)
CREATE NONCLUSTERED INDEX NCI_Part_Price_Log_PartID_Date ON Part_Price_Log (PartID, ModifiedDate)

Об этом последнем заключении является то, что операции вставки будут занимать больше времени (поскольку индекс также должен обновляться), а размер таблицы будет увеличиваться из-за индексов.

Также имейте в виду, что если ваши данные допускают многократное обновление одной и той же детали за один и тот же день, то использование соединения PRIMARY KEY приведет к сбою 2-го обновления. Здесь вы можете выбрать суррогатный ключ, использовать DATETIME вместо DATE (даст вам больше полей для обновлений) или CLUSTERED INDEX без ограничения PRIMARY KEY или UNIQUE.


Я бы предложил сделать следующее. Вы сохраняете только один индекс (фактическая таблица, поскольку она кластеризована), порядок всегда вставляется, вам не нужно беспокоиться о повторении ModifiedDate с тем же PartID, и ваши запросы по дате будут быстрыми.

CREATE TABLE Part_Price_Log (
    LogID INT IDENTITY PRIMARY KEY NONCLUSTERED,
    ModifiedDate DATE,
    PartID INT)

CREATE CLUSTERED INDEX NCI_Part_Price_Log_Date_PartID ON Part_Price_Log (ModifiedDate, PartID)
0 голосов
/ 26 апреля 2018

Я согласен, что в этом сценарии лучше сохранить столбец идентификаторов / uniqueidentifier в качестве первичного ключа. Также, если вы зададите partid и date в качестве составного первичного ключа, произойдет сбой в случае, когда два одновременно работающих пользователя попытаются обновить цена за единицу в то же время. Первичный ключ в этом случае потерпит неудачу. Поэтому лучшим подходом будет иметь столбец идентификаторов в качестве первичного ключа и продолжать сбрасывать изменения в журнальной таблице. В случае, если вы столкнетесь с некоторыми барьерами производительности позже вы можете разбить таблицу на год и решить эту проблему производительности.

0 голосов
/ 26 апреля 2018

Не зная вашего домена, советовать очень сложно. Как вы определяете роль в реальном мире? Давайте предположим, что вы используете EAN. Это ваш «естественный ключ». Теперь, получает ли деталь новый EAN каждый раз, когда цена меняется? Вероятно, нет, и в этом случае реальный мировой идентификатор цены детали представляет собой совокупность его EAN и периода времени, в течение которого эта цена действовала.

Я думаю, что комментарий о «простом способе получения ошибок в ваших данных» относится к тому факту, что временные базы данных не только более сложны по своей природе (они имеют дополнительное измерение - время), но и поддерживают временную функциональность. отсутствует в большинстве СУБД SQL.

Например, ваш продукт SQL имеет тип данных interval, или вам нужно свернуть свой собственный, используя пару столбцов start_date и end_date? Имеет ли ваш выбранный продукт SQL возможность ограничения внутри таблицы, например: предотвратить наложение или несовпадение интервалов для одной и той же детали? Есть ли в вашем продукте SQL временные функции для простого запроса временных данных?

0 голосов
/ 26 апреля 2018

В общем, я думаю, что лучшие первичные ключи - это синтетические автоматически увеличивающиеся ключи. У них есть определенные преимущества:

  • Значение ключа записывает порядок вставки.
  • Ключи имеют фиксированную длину (обычно 4 байта).
  • Отдельные ключи намного проще для ссылок на внешние ключи.
  • В базах данных (таких как SQL Server по умолчанию), которые кластеризуют данные на основе первичного ключа, вставки идут "в конце".
  • Их относительно легко набирать и сравнивать (мои глаза просто плохо работают для сравнения UUID).

Четвертый из них - действительно серьезная проблема в базе данных, которая содержит много вставок, как показывают ваши данные.

В составных первичных ключах нет ничего априори . Они иногда полезны. Но я бы не пошел в этом направлении.

...