хранение еженедельных целей в базе данных - PullRequest
1 голос
/ 10 декабря 2010

у меня есть следующее требование

Sales Officer: Bob

             Week1 Week2  Week3 ................. Week52
    Prod1     10    15    12    .................    14
    Prod2     20    14    10    .................    17
    .                                                 .   
    .                                                 .
    .                                                 .

Менеджер по продажам будет устанавливать цели для каждого сотрудника по продажам еженедельно. Менеджер по продажам может вводить фактические продажи ежедневно для каждого продукта через аналогичную сетку в соответствии с установленными целями, например,
Редактировать
В приведенном выше случае Supervisor установил целевой показатель в 10 единиц на неделю 1. Теперь сотрудник по продажам будет вводить данные о продажах на ежедневной основе как 1,2,0,1,3,2 = 9 (фактическая продажа за неделю 1) против целевого показателя. из 10 единиц он продал 9 единиц в первую неделю.

Я уже создал таблицы Employee и Product. Может ли кто-нибудь рассказать о передовой практике хранения 10101 * дней и недель в базе данных, по которой сохраняются целевые показатели и могут быть записаны фактические продажи.

Я думаю о сохранении данных в следующей таблице

EmpSales (EmployeeID,ProductID,SaleTarget,Actual Sale,Date,WeekNo,Month)

Заранее спасибо

Ответы [ 3 ]

3 голосов
/ 13 декабря 2010

Это действительно просто с точки зрения чистого реляционного моделирования. Я не вижу необходимости в какой-либо «денормализации».

Модель данных о продажах .

Если вы не знакомы со Стандартом для моделирования реляционных баз данных, может помочь IDEF1X Notation .

Чистый 5NF; полная декларативная ссылочная целостность; нет нулей, нет обновлений аномалий; нет GROUP BYs; Чистая арифметика Даты.

  • SaleTarget сравнивается с SaleActual по проекции и может быть в том же наборе результатов.

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

  • Я говорю 5NF, потому что это минимум, который я предоставляю для устранения аномалий обновления, и большинству разработчиков моделей это знакомо. Но если вас это не пугает, две таблицы продаж на самом деле Шестая нормальная форма .

  • Это позволяет выполнять полное разворот (недели или месяцы сверху; продукты или сотрудники в стороне; наоборот; любая комбинация) без временных таблиц или сложного SQL. (Просто спросите.)

Я думаю, что это может быть даже самоочевидным, но я предоставлю Глагольные фразы , в которых прописаны бизнес-правила, только потому, что в каждом участвуют три родителя:

  • У каждого сотрудника запланирована распродажа товара на неделю
  • Каждый продукт запланирован SaleTarget по сотрудникам на неделю
  • Каждый сотрудник совершил продажу продукта в день
  • Каждый продукт прошел SaleActual по сотрудникам в день

Наслаждайтесь.

Сравнение

  1. Я должен был упомянуть. Обратите внимание, что нет вертикального (строки) или горизонтального (столбцы) дублирования. Когда столбцы дублируются, например, StartDate и EndDate, вы нарушаете 3NF (введены функциональные зависимости) и вводите аномалию обновления. EndDate в любом ряду - это StartDate в следующем ряду (то есть минус 1 секунда считается обманом, является ухищрением); при обновлении теперь нужно менять две строки вместо одной. Что еще более важно, эта структура настолько проста (это не временные ряды или «временные» требования), EndDate не требуется.

Ответ на комментарий

  1. Модель данных была обновлена ​​с учетом требований Month и Year. Теперь вам нужно проверить ограничение на SaleTarget, чтобы убедиться, что DateType равно W в течение недели. Загрузка таблицы Date проста: вам не нужен бессмысленный код (повторяющиеся вручную вставки), который публикуется в SQLTeam; они известны тем, что они глупы и не соответствуют стандартам.

  2. Таблица SaleActual теперь содержит значения Daily, Weekly, Monthly и Year. Что, конечно, вы суммируете программно в первый день каждой недели, месяца, дня. Сначала добавьте новую строку в Date.

  3. 5NF - это минимум, необходимый для соответствия стандарту в наши дни, поэтому вам нужно к этому привыкнуть. В основном, среди академиков было много споров (плюс такие выгребные ямы, как вики, публикующие совершенно неверные записи) о НФ между 3НФ и 5НФ. Короткое и приятное определение 5NF состоит в том, что это то, что 3NF предполагалось, с нулевым дублированием данных, с нулевыми аномалиями обновления (нет дублированных столбцов, которые должны обновляться транзакционно).

  4. Забудьте о 6NF сейчас. Любая таблица, которая находится в 6NF, находится в 5NF (и 4NF и BCNF и 3NF). Просто относитесь к двум таблицам продаж как 5NF. Когда вам нужно написать сводный отчет, скажем, через год, именно тогда вы поймете ценность этой структуры. Пожертвуйте моей любимой благотворительности или накормите бездомного.

2 голосов
/ 10 декабря 2010

Я бы лично сохранял цели и фактические данные в отдельных строках, и, скорее всего, в отдельных таблицах:

Цели: EmployeeId, PeriodId, ProductId, TargetValue

Продажи: EmployeeId, PeriodId, ProductId, SalesValue

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

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

Таким образом, ActualSales будет выглядеть примерно так (только с обычной таблицей Period, которая сама может быть таблицей периодов и дат):

SELECT sp.EmployeeId
       , p.ProductId
       , pd.PeriodType
       , pd.PeriodId
       , SUM(id.Quantity * id.UnitProce) AS TotalSales
FROM Invoice AS i
INNER JOIN InvoiceDetail AS id
    ON id.InvoiceId = i.InvoiceId
INNER JOIN Employee AS sp
    ON sp.EmployeeId = i.SalesPersonId
INNER JOIN Product AS p
    ON id.ProductId = p.ProductId
INNER JOIN Period AS pd
    ON pd.StartDate <= i.InvoiceDate
    AND pd.EndDate > i.InvoiceDate
GROUP BY sp.EmployeeId, p.ProductId, pd.PeriodType, pd.PeriodId

В этом случае данные дублировались бы, если бы у вас были перекрывающиеся периоды (например, ежедневные, еженедельные, ежемесячные), поэтому вам нужно будет агрегировать ТОЛЬКО один тип периодов - вот почему я специально включил их вэто примерное представление, хотя здесь это избыточно.

Я ожидаю, что общая таблица периодов будет выглядеть следующим образом:

PeriodId
PeriodType
StartDate
EndDate

Это будет заполнено различными периодами, о которых вы хотите сообщить:

'Q', 1/1/2010, 4/1/2010
'M', 1/1/2010, 2/1/2010
'M', 2/1/2010, 3/1/2010
'M', 3/1/2010, 4/1/2010
'W', 1/3/2010, 1/10/2010
'W', 1/10/2010, 1/17/2010
etc.
'D', 1/1/2010, 1/2/2010
'D', 1/2/2010, 1/3/2010
etc.

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

Calendar
DateId
Date
IsHoliday

Затем вы можете включить это при присоединении для подсчета количества выходных / праздничных дней в периоде и т. Д.

Обычно этобухгалтерский учет / бизнес, но вы можете посмотреть на стандартизацию вашего календаря.Например, в медиа-покупках для телевизионной рекламы они делают каждый «квартал» равным и стандартизируют каждый «месяц» - 4 недели, 4 недели, 5 недель.Очевидно, что они делают исключения для праздничных и специальных телевизионных мероприятий, но это помогает сгладить учет и легче сравнивать периоды.

1 голос
/ 10 декабря 2010

Лично я бы пошел за более общей таблицей «периодов».

period (periodId, startDate, endDate, weekNo, Month, Year)

и затем добавил бы

empSales (EmployeeId, ProductId, SaleTarget, ActualSale, periodId).

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

Подведение итогов должно быть проще, поскольку при условии, что продажи отсортированы по дням, суммирование между интервалами будет проще, если вы не хотите дублировать поле «недели» во всей БД.

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

Например, вы можете установить недельный целевой показатель на неделю с 22 по 28 ноября (я использую Европейское соглашение о начале недели в понедельник) и установить специальный однодневный период в Черную пятницу

Итак:

Period:  

 periodId|startDate  |endDate    |weekNo|Month    |  Year|
  0030020|22-NOV-2010|28-NOV-2010|  43  |November | 2010 |
  0030026|26-NOV-2010|26-NOV-2010| null |November | 2010 |

empSales:

 EmployeeId|ProductId|SaleTarget|ActualSale|periodId|
     567689|   788585|      58  |       42 | 0030020|
     567689|   788585|      28  |       32 | 0030026|

Обратите внимание, как сотрудник 567689 пропустил свою недельную цель, но сумел преодолеть свою цель в Черную пятницу.

Кстати, работая над этим примером, я думаю, вам лучше отказатьсятаблицу «empSales», переименовав ее в «empTargets»:

empTargets (EmployeeId, ProductId, SaleTarget, periodId).

, потому что фактические продажи легко рассчитываются на лету либо с помощью UDFили в виде - в конце концов, это просто

select sum(items_sold) 
from sales 
where sales.employeeId = empTargets.employeeId and 
      sales.ProductId= empTargets.ProductId and 
      sales.saleDate between empTargets.startDate and 
                             empTargets.endDate)

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

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