2

これは頭​​が痛いです!:P

テーブルがassignmentsあり、割り当てに基づいてメンバーの期間を計算したいと考えています。簡略化された形式では、これは比較的単純です。

-------------------------------------------------------------------------
| id    | member_id | unit_id   | start_date    | end_date  |
-------------------------------------------------------------------------
| 1 | 2     | 23        | 2013-01-01    | 2013-02-01    |
-------------------------------------------------------------------------
| 2 | 2     | 25        | 2013-02-01    | 2013-03-01    |
-------------------------------------------------------------------------
| 3 | 2     | 27        | 2013-03-01    | NULL      |
-------------------------------------------------------------------------

これはSUM()DATEDIFF()onstart_dateとを実行するだけの問題ですend_date。問題は、メンバーが同時割り当てを持つ可能性があることです。

-------------------------------------------------------------------------
| id    | member_id | unit_id   | start_date    | end_date  |
-------------------------------------------------------------------------
| 1 | 2     | 23        | 2013-01-01    | 2013-02-01    |
-------------------------------------------------------------------------
| 2 | 2     | 25        | 2013-02-01    | 2013-03-01    |
-------------------------------------------------------------------------
| 3 | 2     | 30        | 2013-02-15    | 2013-03-01    |*
-------------------------------------------------------------------------
| 4 | 2     | 27        | 2013-03-01    | NULL      |
-------------------------------------------------------------------------

ここで、#3 が #2 と同じ時期に発生したことをどうにか認識しなければならないので、SUM().

さらに、メンバーのデュレーションにギャップがある場合はどうなるでしょうか?

-------------------------------------------------------------------------
| id    | member_id | unit_id   | start_date    | end_date  |
-------------------------------------------------------------------------
| 1 | 2     | 23        | 2013-01-01    | 2013-02-01    |
-------------------------------------------------------------------------
| 2 | 2     | 25        | 2013-02-01    | 2013-02-05    |*
-------------------------------------------------------------------------
| 3 | 2     | 30        | 2013-02-15    | 2013-03-01    |*
-------------------------------------------------------------------------
| 4 | 2     | 27        | 2013-03-01    | NULL      |
-------------------------------------------------------------------------

また、NULL「現在」を意味するため、CURDATE().

何か案は?

4

2 に答える 2

1

これがアイデアです。各レコードを 2 つに分割して、割り当ての開始日と終了日のリストを取得します。次に、特定の日にアクティブな割り当ての数を決定します。基本的には、開始ごとに「1」、終了ごとに「-1」を追加し、累積合計を取ります。

次に、最終的な集計を行う前に、次の日付がいつ生理を取得するかを決定する必要があります。

最初の部分は、次のクエリによって処理されます。

select member_id, thedate,
       @sumstart := if(@prevmemberid = memberid, @sumstart + isstart, isstart) as sumstart,
       @prevmemberid := memberid
from (select member_id, start_date as thedate, 1 as isstart
      from assignments
      union all
      select member_id, end_date, -1 as isstart
      from assignments
      order by member_id, thedate
     ) a cross join
     (select @sumstart := 0, @prevmemberid := NULL) const;

残りはさらに変数を使用します。

select member_id,
       sum(case when sumstart > 0 then datediff(nextdate, thedate) end) as daysactive
from (select member_id, thedate, sumstart,
         if(@prevmemberid = memberid, @nextdate, NULL) as nextdate,
         @prevmemberid := memberid,
         @nextdate = thedate
      from (select member_id, thedate,
                   @sumstart := if(@prevmemberid = memberid, @sumstart + isstart, isstart) as sumstart,
                   @prevmemberid := memberid
            from (select member_id, start_date as thedate, 1 as isstart
                  from assignments
                  union all
                  select member_id, coalesce(end_date, CURDATE()), -1 as isstart
                  from assignments
                  order by member_id, thedate
                 ) a cross join
                 (select @sumstart := 0, @prevmemberid := NULL) const;
           ) a cross join
           (select @nextmemberid := NULL, @nextdate := NULL) const
       order by member_id, thedate desc;
      ) a
group by member_id;

このように変数を使用するのは好きではありません。なぜなら、MySQL は特定の .xml 内の変数割り当ての順序を保証しないからselectです。ただし、実際には、記述された順序で評価されます (このクエリはこれに依存します)。これは変数なしで記述できますが、with文、ウィンドウ関数、またはfrom句でサブクエリを受け取るビューさえもなしで記述できますが、結果の SQL ははるかに醜いものになります。

于 2013-09-02T19:23:34.807 に答える
0

重複する割り当てをフィルターで除外するのは、SQL よりもコードの方が簡単だと思います。特定の member_id のすべての割り当てを start_date 順に並べて取得できます。

select * from assignments where member_id='2' order by start_date asc

次に、これらの割り当てをループして、重複する割り当てを除外できます。A が B の開始前に終了する場合、または B が A の開始前に終了する場合、2 つの割り当て A および B は重複していません。

開始日に従って結果を並べ替えたため、2 番目のケースは無視しても問題ありません。B は A より前に開始されることはないため、A が開始する前に終了することはありません。次に、次のようなものを取得します。

for i=0..assignments.length
    for j=i+1..assignments.length
        if (assignments[j].start_date < assignments[i].end_date)
            assignments[j] = null; // it overlaps -> get rid of it

次に、割り当てをループし、null 以外の割り当ての期間を合計します。これは簡単なはずです

于 2013-09-02T19:29:52.837 に答える