разработка базы данных электронных заявок на отпуск - PullRequest
0 голосов
/ 08 октября 2008

В настоящее время я работаю над разрешением на отпуск (которое является подмножеством моего проекта электронного планировщика), и моя база данных выглядит следующим образом:

event (event_id, dtstart, dtend... *follows icalendar standard*)

event_leave (event_id*, leave_type_id*, total_days)

_leave_type (leave_type_id, name, max_carry_forward)

_leave_allocation (leave_allocation_id, leave_type_id*, name, user_group_id, total_days, year)

_leave_carry_forward(leave_carry_forward_id, leave_type_id*, user_id, year)

Кто-нибудь здесь, в stackoverflow, также работает над приложением электронного выхода? Не забудьте поделиться своим дизайном базы данных, так как я ищу лучший дизайн, чем мой. Проблема с моим текущим дизайном возникает только в начале года, когда система вычисляет количество дней, которые можно перенести.

В общей сложности мне нужно было бы выполнить 1 + {$ number_of users} * 2 запроса (первый, чтобы узнать количество правил распределения и максимальную квоту для переноса. Затем для каждого пользователя мне нужно выяснить баланс, а затем вставить баланс в базу данных)

Ответы [ 3 ]

2 голосов
/ 08 октября 2008

Я не очень хорошо слежу за схемой (похоже, что у каждого типа_воли есть перенос)? В таблицах событий * нет пользователя?), Но вы должны иметь возможность динамически определять баланс в любой момент времени - в том числе по годам.

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

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

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

User { User_Id (PK) }

// Year may be a tricky business logic issue here...Do you charge the Start or End year
// if the event crosses a year boundary? Or do you just do 2 different events?
// You want year in this table, though, so you can do a FK reference to Leave_Allocation
// Some RDBMS will let you do a FK from a View, though, so you could do that
Event { Event_Id (PK), User_Id, Leave_Type_Id, Year, DtStart, DtEnd, ... 
   // Ensure that events are charged to leave the user has
   FK (User_Id, Leave_Type_Id, Year)->Leave_Allocation(User_Id, Leave_Type_Id, Year)
}

Leave_Type { Leave_Type_Id, Year, Max_Carry_Forward 
   // Max_Carry_Forward would probably change per year
   PK (Leave_Type_Id, Year)
}

// Starting balance for each leave_type and user, per year
// Not sure the name makes the most sense - I think of Allocated as used leave,
// so I'd probably call this Leave_Starting_Balance or something
Leave_Allocation { Leave_Type_Id (FK->Leave_Type.Leave_Type_Id), User_Id (FK->User.User_Id), Year, Total_Days 
   PK (Leave_Type_Id, User_Id, Year)
   // Ensure that leave_type is defined for this year
   FK (Leave_Type_Id, Year)->Leave_Type(Leave_Type_Id, Year)
}

А затем, представления (где вы можете применить некоторую денормализацию):

/* Just sum up the Total_Days for an event to make some other calcs easier */
CREATE VIEW Event_Leave AS
   SELECT
      Event_Id,
      User_Id,
      Leave_Type_Id,
      DATEDIFF(d, DtEnd, DtStart) as Total_Days,
      Year
   FROM Event

/* Subtract sum of allocated leave (Event_Leave.Total_Days) from starting balance (Leave_Allocation) */
/* to get the current unused balance of leave */
CREATE VIEW Leave_Current_Balance AS
   SELECT
      Leave_Allocation.User_Id,
      Leave_Allocation.Leave_Type_Id,
      Leave_Allocation.Year,
      Leave_Allocation.Total_Days - SUM(Event_Leave.Total_Days) as Leave_Balance
   FROM Leave_Allocation
   LEFT OUTER JOIN Event_Leave ON
      Leave_Allocation.User_Id = Event_Leave.User_Id
      AND Leave_Allocation.Leave_Type_Id = Event_Leave.Leave_Type_Id
      AND Leave_Allocation.Year = Event_Leave.Year
   GROUP BY
      Leave_Allocation.User_Id,
      Leave_Allocation.Leave_Type_Id,
      Leave_Allocation.Year,
      Leave_Allocation.Total_Days

Теперь наш запрос Leave CarryForward - это просто минимальное текущее сальдо или максимальное перенесенное на 1/1 максимальное время переноса на 1/1.

   SELECT
      User_Id,
      Leave_Type_Id,
      Year,
      /* This is T-SQL syntax...your RDBMS may be different, but should be able to do the same thing */
      /* If not, you'd do a UNION ALL to Max_Carry_Forward and select MIN(BalanceOrMax) */
      CASE 
         WHEN Leave_Balance < Max_Carry_Forward 
             THEN Leave_Balance 
         ELSE 
             Max_Carry_Forward 
      END as Leave_Carry_Forward
  FROM Leave_Current_Balance
  JOIN Leave_Type ON
      Leave_Current_Balance.Leave_Type_Id = Leave_Type.Leave_Type_Id
      /* This assumes max_carry_forward is how much you can carry_forward into the next year */
      /* eg,, a max_carry_forward of 300 hours for year 2008, means I can carry_forward up to 300 */
      /* hours into 2009. Otherwise, you'd join on Leave_Current_Balance.Year + 1 if it's how much */
      /* I can carry forward into *this* year. */
      AND Leave_Current_Balance.Year = Leave_Type.Year

Таким образом, в конце года вы вставляете балансы CarryForward обратно в LeaveAllocation с новым годом.

0 голосов
/ 09 октября 2008

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

Дизайн стола

Это основная таблица (в основном на основе схемы iCalendar), в которой хранится событие. Событие может быть типичным событием или встречей, праздничным днем ​​и т. Д.

event (event_id (PK), dtstart, dtend, ... --other icalendar fields--)

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

event_leave (event_id (PK/FK->event), total_days, leave_type_id (FK->leave_type))

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

leave_type (leave_type_id (PK), name, require_support, require_recommend, max_carry_forward)

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

leave_allocation (leave_allocation_id, year(PK), leave_type_id (PK/FK->leave_type), total_days, group_id)

Следующая таблица для хранения информации о переносе. Эта таблица будет заполняться один раз в год для каждого пользователя . Эта таблица будет заполняться раз в год, так как расчет на лету не прост. Формула подсчета оставляемой_карты для пользователя:

leave_carry_forward(2009) = min(leave_allocation(2008) + leave_carry_forward(2007) - leave_taken(2008), maximum_carry_forward());

leave_carry_forward (leave_carry_forward_id, user_id, year, total_days)

Некоторые примеры использования и решения

Рассчитать баланс (WIP)

Для расчета баланса я делаю запрос к представлению, объявленному следующим образом

DROP VIEW IF EXISTS leave_remaining_days;
CREATE OR REPLACE VIEW leave_remaining_days AS
    SELECT      year, user_id, leave_type_id, SUM(total_days) as total_days
    FROM        (
            SELECT  allocated.year, usr.uid AS "user_id", allocated.leave_type_id, 
                allocated.total_days
            FROM    users usr
                JOIN app_event._leave_allocation allocated
                ON allocated.group_id = usr.group_id
            UNION
            SELECT  EXTRACT(year FROM event.dtstart) AS "year", event.user_id, 
                leave.leave_type_id, leave.total_days * -1 AS total_days
            FROM    app_event.event event
                LEFT JOIN app_event.event_leave leave
                ON event.event_id = leave.event_id
            UNION
            SELECT  year, user_id, leave_type_id, total_days
            FROM    app_event._leave_carry_forward
        ) KKR
    GROUP BY    year, user_id, leave_type_id;

Заполнить таблицу left_allocation в начале года

public function populate_allocation($year) {
    return $this->db->query(sprintf(
        'INSERT INTO %s (%s)' .
            "SELECT '%s' AS year, %s " .
            'FROM   %s ' .
            'WHERE  "year" = %s',
        'event_allocation',
        'year, leave_type_id, total_days ...', //(all the fields in the table)
        empty($year) ? date('Y') : $year,
        'leave_type_id, total_days, ..', //(all fields except year)
        $this->__table,
        empty($year) ? date('Y') - 1 : $year - 1
    ))
    ->count() > 0;  // using the database query builder in Kohana PHP framework
}

Заполнить таблицу left_carry_forward в начале года

Узнать тип отпуска, назначенный пользователю

Мне, вероятно, нужно переименовать это представление (я плохо умею называть вещи ...). На самом деле это таблица длявола для пользователя.

DROP VIEW IF EXISTS user_leave_type;
CREATE OR REPLACE VIEW user_leave_type AS
    SELECT  la.year, usr.uid AS user_id, lt.leave_type_id, lt.max_carry_forward
    FROM    users usr
            JOIN app_event._leave_allocation la
                JOIN app_event._leave_type lt
                ON la.leave_type_id = lt.leave_type_id
            ON usr.group_id = la.group_id

Фактический запрос

INSERT INTO leave_carry_forward (year, user_id, leave_type_id, total_days)
    SELECT      '{$this_year}' AS year, user_id, leave_type_id, MIN(carry_forward) AS total_days
    FROM        (
                    SELECT  year, user_id, leave_type_id, total_days AS carry_forward
                    FROM    leave_remaining_days
                    UNION
                    SELECT  year, user_id, leave_type_id, max_carry_forward AS carry_forward
                    FROM    user_leave_type
                ) KKR
    WHERE       year = {$last_year}
    GROUP BY    year, user_id, leave_type_id;
0 голосов
/ 08 октября 2008

Всегда лучший дизайн !!

Ваш текущий дизайн работает? Сколько пользователей вы ожидаете (т.е. имеет ли значение, что вам нужно будет выполнить x тысяч запросов).

Если проблема текущего дизайна только в начале года, то, возможно, вы могли бы с этим жить!

Приветствия

НЗС

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