3

私は単純な時間追跡アプリに取り組んでいます。

従業員の入退室時刻を記録するテーブルを作成しました。

私のデータが現在どのように見えるかの例を次に示します。

E_ID | In_Out |      Date_Time
------------------------------------
  3  |   I    | 2012-08-19 15:41:52
  3  |   O    | 2012-08-19 17:30:22
  1  |   I    | 2012-08-19 18:51:11
  3  |   I    | 2012-08-19 18:55:52
  1  |   O    | 2012-08-19 20:41:52
  3  |   O    | 2012-08-19 21:50:30

次のように、従業員の IN 時間と OUT 時間を 1 つの行にペアにするクエリを作成しようとしています。

E_ID |       In_Time       |      Out_Time
------------------------------------------------
  3  | 2012-08-19 15:41:52 | 2012-08-19 17:30:22
  3  | 2012-08-19 18:55:52 | 2012-08-19 21:50:30
  1  | 2012-08-19 18:51:11 | 2012-08-19 20:41:52

ここで何を達成しようとしているのかが明確になっていることを願っています。基本的に、インとアウトの両方の時間を 1 行にまとめたレポートを生成したいと考えています。

これについての助けをいただければ幸いです。前もって感謝します。

4

2 に答える 2

4

私が考えることができる3つの基本的なアプローチがあります。

1 つのアプローチでは MySQL ユーザー変数を使用し、1 つのアプローチでは theta JOIN を使用し、別のアプローチでは SELECT リストのサブクエリを使用します。

シータ結合

1 つのアプローチは、theta-JOIN を使用することです。このアプローチは、複数の RDBMS で機能する一般的な SQL アプローチ (MySQL 固有の構文ではない) です。

注: 行数が多い場合、この方法では非常に大きな中間結果セットが作成される可能性があり、パフォーマンスに問題が生じる可能性があります。

SELECT o.e_id, MAX(i.date_time) AS in_time, o.date_time AS out_time    
  FROM e `o`
  LEFT
  JOIN e `i` ON i.e_id = o.e_id AND i.date_time < o.date_time AND i.in_out = 'I'
 WHERE o.in_out = 'O'
 GROUP BY o.e_id, o.date_time
 ORDER BY o.date_time

これが行うことは、従業員のすべての「O」行を以前のすべての「I」行と照合し、次に MAX 集計を使用して最も近い日時の「I」レコードを選択することです。

これは、完全にペアになったデータに対して機能します。不完全なペアに対して奇妙な結果が生じる可能性があります... (中間の「I」行がない 2 つの連続する「O」レコードは、両方とも同じ「I」行に一致するなど)


SELECT リスト内の相関サブクエリ

もう 1 つの方法は、SELECT リストで相関サブクエリを使用することです。これはパフォーマンスが最適ではない可能性がありますが、実行可能な場合もあります (指定された結果セットを返す最速の方法である場合もあります... このアプローチは、外側のクエリで返される行数が限られている場合に最適です)。

 SELECT o.e_id
      , (SELECT MAX(i.date_time)
           FROM e `i`
          WHERE i.in_out = 'I'
            AND i.e_id = o.e_id
            AND i.date_time < o.date_time
        ) AS in_time
      , o.date_time AS out_time
   FROM e `o`
  WHERE o.in_out = 'O'
  ORDER BY o.date_time

ユーザー変数

もう 1 つのアプローチは、MySQL ユーザー変数を利用することです。(これは MySQL 固有のアプローチであり、「欠落している」分析関数に対する回避策です。)

このクエリが行うことは、すべての行を e_id で並べ替え、次に date_time で並べ替えて、順番に処理できるようにすることです。「O」(out) 行に遭遇するたびに、直前の「I」行の date_time の値を「in_time」として使用します)

注意: MySQL ユーザー変数のこの使用法は、MySQL が特定の順序、つまり予測可能な計画で操作を実行することに依存しています。インライン ビュー (MySQL 用語では「派生テーブル」) を使用すると、予測可能な実行計画が得られます。ただし、この動作は MySQL の将来のリリースで変更される可能性があります。

SELECT c.e_id
     , CAST(c.in_time AS DATETIME) AS in_time
     , c.out_time
  FROM (
         SELECT IF(@prev_e_id = d.e_id,@in_time,@in_time:=NULL) AS reset_in_time
              , @in_time := IF(d.in_out = 'I',d.date_time,@in_time) AS in_time
              , IF(d.in_out = 'O',d.date_time,NULL) AS out_time
              , @prev_e_id := d.e_id  AS e_id
           FROM (
                  SELECT e_id, date_time, in_out 
                    FROM e
                    JOIN (SELECT @prev_e_id := NULL, @in_time := NULL) f
                   ORDER BY e_id, date_time, in_out 
                 ) d
       ) c
 WHERE c.out_time IS NOT NULL
 ORDER BY c.out_time

これは、あなたが持っているデータのセットに対して機能します。行が完全にペアになっていない場合 (たとえば、2 つの 'O' 行と 'I' 行がない場合) は、風変わりなデータで必要な結果セットを確実に取得するために、より徹底的なテストと微調整が必​​要です。それらの間に、後続の「O」行がない「I」行など)

SQL フィドル

于 2012-08-24T21:17:30.140 に答える
2

残念ながら、MySQL には SQL Server のような機能がありませんROW_NUMBER() OVER(PARTITION BY ORDER BY()。または、これは信じられないほど簡単です。

しかし、MySQL でこれを行う方法があります。

set @num := 0, @in_out := '';

select emp_in.id,
  emp_in.in_time,
  emp_out.out_time
from 
(
  select id, in_out, date_time in_time, 
     @num := if(@in_out = in_out, @num + 1, 1) as row_number,
     @in_out := in_out as dummy
  from mytable
  where in_out = 'I'
  order by date_time, id
) emp_in
join
(
  select id, in_out, date_time out_time,
     @num := if(@in_out = in_out, @num + 1, 1) as row_number,
     @in_out := in_out as dummy
  from mytable
  where in_out = 'O'
  order by date_time, id
) emp_out
  on emp_in.id = emp_out.id
  and emp_in.row_number = emp_out.row_number
order by emp_in.id, emp_in.in_time

基本的に、これにより 2 つのサブクエリが作成され、それぞれがその特定のレコードの row_number を生成します。1 つのサブクエリは in_time 用で、もう 1 つは out_time 用です。

次に、とJOINで 2 つのクエリを一緒に実行します。emp_idrow_number

デモで SQL Fiddle を参照してください

于 2012-08-24T21:52:54.910 に答える