私が考えることができる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 フィドル