Как смоделировать необязательную разбивку столбца - PullRequest
1 голос
/ 21 февраля 2012

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

Один из столбцов, который нужно сохранить, скажем,количество продаж в день (с разбивкой по различным атрибутам).С последними данными мы можем разбить это на онлайн-продажи и продажи в магазине;однако до определенного сокращения единственной информацией, доступной для заполнения этой базы данных, является общий объем продаж без разбивки.

Я не могу придумать особенно элегантного способа представить это, чтобы новые данныеможет заполнять логические столбцы «Продажи через Интернет» и «Продажи в магазине», при этом «Сумма продаж» вычисляется как их сумма (в столбце view / sproc / computed) - и все же старые данные могут просто сообщать общую сумму продаж.

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

Есть ли канонический способ справиться с этой ситуацией?


Учитывая отсутствие сильных ответов, я опубликую несколько своих собственных ответов, которые я считаю кандидатами (в конечном итоге мне может понадобиться принять один из них, если не получится превосходных ответов).Комментарии, критика и / или голосование по ним любезно приняты - и особенно их улучшения.

Ответы [ 5 ]

1 голос
/ 22 февраля 2012

То, что вы описываете, - это разница между OLTP и OLAP базой данных.

OLTP ( Обработка транзакций в режиме онлайн ) Этот тип данных представляет ежедневные транзакции. Например, инвентаризации, изменения, удаления. Клиент добавляет в корзину запросы, заказы, возвраты. Это самые мелкие транзакции, которые происходят весь день.

OLAP ( Аналитическая обработка в режиме реального времени ). Этот тип данных представляет собой накопленные данные за заданные периоды времени. Например: ежедневно, еженедельно, ежемесячно, ежеквартально, ежегодно. Купив эту информацию в отдельном наборе таблиц или даже в базе данных, вы можете запускать различные запросы, чтобы получить нужные вам отчеты.

Проблема, с которой вы можете столкнуться, заключается в том, что вам нужна информация OLAP, когда у вас есть только данные OLTP.

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

Каждый месяц вы можете запускать разные процессы для создания ежемесячных таблиц OLAP.

Поначалу это довольно сложная работа, но она дает вам лучшее из обоих миров. Вы можете играть в игры «что делать, если» весь день против ваших данных OLAP, не влияя на клиентов или повседневные операции.

0 голосов
/ 22 февраля 2012

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

Таким образом, схема может выглядеть следующим образом:

DataFact
-------------
DataFactId (PK)
(+any other fact columns apart from sales)

SalesData
---------
DataFactId (FK to DataFact)
SalesDataType ("Total"/"Online" etc - either as varchar or FK to dimension table)
SalesValue (the actual sales figure we want to record)

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

Недостатком является то, что он по-прежнему не выражает ограничения на то, что должен существовать общий или онлайн + в магазине.На самом деле мы даже не можем выразить, что хотя бы одна запись SalesData должна существовать;это имеет те же проблемы, что и добавление нескольких обнуляемых полей.И хотя этот подход довольно опрятен, чтобы наилучшим образом использовать его гибкость, мы бы хотели, чтобы данные о продажах возвращались запрашивающему клиенту как своего рода сбор, что делает запрос более сложным, чем просто стандартный набор результатов 2D.Это можно было бы сжать обратно в 2D-таблицу, используя агрегирование по таблице SalesData, но я считаю, что вам придется тянуть его несколько раз с разными ограничениями, чтобы определить каждое поле.

0 голосов
/ 22 февраля 2012

Другой возможный подход состоит в том, чтобы иметь две отдельные таблицы фактов, по одной для старых и новых данных, и моделировать их на основе того, какие именно данные могут быть определены в каждом случае.

Для запросов общий результат будет представлять собой объединение данных из обеих таблиц фактов - с выбором из старой таблицы фактов, дополненным NULL (или аналогичным) для конкретных столбцов данных продаж.

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

0 голосов
/ 22 февраля 2012

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

При запросе общая сумма продаж может быть условно заполнена, что-то вроде:

CASE WHEN TotalSales IS NULL THEN OnlineSales + InStoreSales ELSE TotalSales END

Преимущество этого метода в том, что он самый простой с точки зрения приложения.Хотя с точки зрения моделирования данных мне не нравится тот факт, что каждая запись оставит по крайней мере одно поле пустым.И становится трудно выразить ограничение целостности данных, что или TotalSales заполнено, или , оба из OnlineSales и InStoreSales должны быть заполнены.Будет ли проверка этого с помощью триггера считаться хорошей практикой?

(Альтернативной версией этого является заполнение поля TotalSales даже в новом случае, но я не думаю, что дублирование и риск потенциально противоречивых данныхстоит немного проще запрашивать.)

0 голосов
/ 21 февраля 2012

Я бы, возможно, добавил бы таблицу «версия для продажи», которая позволяла бы различать «исторические нерушимые продажи» и «новые продажи».

Итак, может быть, такая структура может быть достигнута:

таблица sales_version

столбцы: salesid, salesversion

таблица sales_v1

столбцы: salesid, datetime

Полагаю, для каждой продажи подробные данные находятся в ведомой таблице, которая ссылается на идентификатор продаж

таблица sales_v2

столбцы: salesid, datetime, online, instore или, возможно, salesid, datetime, type ('online' или 'instore') или идентификатор типа, который сам по себе ссылается на таблицу типов продаж.

Полагаю, для каждой продажи подробные данные находятся в ведомой таблице, которая ссылается на идентификатор продаж

...