0

私は現在、休暇申請書 (e-scheduler プロジェクトのサブセット) に取り組んでおり、次のようなデータベース設計を行っています。

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)

ここスタックオーバーフローで、e-leave アプリに取り組んでいる人はいますか? 私のデータベースよりも優れたデザインを探しているので、データベースのデザインを共有してください。現在の設計の問題は、システムが繰り越し可能な日数を計算している年の初めにのみ発生します。

合計で、1 + {$number_of users} * 2 クエリを実行する必要があります (最初のクエリは、割り当てルールの数と最大繰越クォータを調べるためのものです。次に、各ユーザーについて、残高を調べる必要があり、次に残高をデータベースに挿入します)

4

3 に答える 3

2

私はスキーマをあまりよくフォローしていません(各leave_typeに繰り越しがあるように見えますか?イベント*テーブルにユーザーがいませんか?)が、任意の時点(数年を含む)で動的にバランスを導き出すことができるはずです。

AAMOF、正規化ルールでは、バランスを導き出すことができる必要があります。その後、パフォーマンスのために非正規化することを選択した場合はあなた次第ですが、設計は計算されたクエリをサポートする必要があります。その場合、年末の繰越の計算は単一セットベースのクエリです。

編集:これに対応するためにスキーマを少し変更する必要があり、ロジックを簡単にするために正規化することを選択しました-ただし、必要に応じて、パフォーマンスのために途中で非正規化を挿入できます。

まず、このシナリオで重要な表...うまくいけば、私の疑似構文が意味をなすでしょう:

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の深夜現在の現在の残高の最小値または最大の繰越額です。

   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に挿入し直します。

于 2008-10-08T11:02:14.963 に答える
0

私のデータベース設計といくつかのユースケースに関する詳細なメモ。

テーブルデザイン

これは、イベントを格納するメイン テーブル (基本的に iCalendar スキーマに基づく) です。イベントは、典型的なイベント、または会議、祝日などです。

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

特定の種類のイベントに追跡しなければならない追加情報がある場合は、別のテーブルで飾ります。たとえば、e-leave 固有の情報を格納するテーブル。(要件の一部として、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_typeに対して休暇を取得できる日数が与えられます。このテーブルに保存されているデータは、毎年入力されます (毎年新しいリビジョン)。ユーザーごとではなく、グループごとに与えられた休暇の数のみを保存します。

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

次は、繰り越し情報を格納するテーブルです。このテーブルは、ユーザーごとに 1 年に 1 回作成されます。その場での計算は容易ではないため、このテーブルは年に 1 回入力されます。ユーザーの leave_carry_forward をカウントする式は次のとおりです。

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;

年の初めに leave_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
}

年の初めに leave_carry_forward テーブルに入力します

ユーザーに割り当てられた休暇の種類を調べる

おそらく、このビューの名前を変更する必要があります (名前の付け方が下手です...)。実際には、ユーザーの leave_allocation テーブルです。

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;
于 2008-10-09T02:00:14.337 に答える
0

より良いデザインが常にあります!!

現在のデザインは機能していますか?何人のユーザーが予想されますか (つまり、何千回もクエリを実行する必要があるかどうか)。

現在のデザインの問題が年の初めだけである場合は、おそらくそれを受け入れることができます!

乾杯

NZS

于 2008-10-08T10:12:56.730 に答える