私は少し時間追跡ウェブアプリを持っています(Rails 3.2.8とMySQLで実装されています)。アプリには、特定の日付に特定のタスクに時間を追加する複数のユーザーがいます。システムは、ユーザーが1つの日付のタスクごとに1回のエントリ(つまり行)しか持てないように設定されています。つまり、同じタスクと日付に2回時間を追加すると、新しい行を作成するのではなく、既存の行に時間が追加されます。一意性はインデックスuser_id/task_id/date
によって強制されます。UNIQUE
今、私は2つのタスクをマージしようとしています。簡単に言うと、タスクID2をタスクID1にマージすると、
time | user_id | task_id | date
------+----------+----------+-----------
10 | 1 | 1 | 2012-10-29
15 | 2 | 1 | 2012-10-29
10 | 1 | 2 | 2012-10-29
5 | 3 | 2 | 2012-10-29
これに変更します
time | user_id | task_id | date
------+----------+----------+-----------
20 | 1 | 1 | 2012-10-29 <-- time values merged (summed)
15 | 2 | 1 | 2012-10-29 <-- no change
5 | 3 | 1 | 2012-10-29 <-- task_id changed (no merging necessary)
つまり、指定されたuser_id / date / taskコンボが競合する時間値を合計することにより、マージします。
ON DUPLICATE KEY UPDATE ...
task_id = 2エントリごとに挿入を行うと、一意の制約を使用してを実行できると思います。しかし、それはかなりエレガントではないようです。
また、タスク1のすべての行を合計時間で最初に更新する方法を見つけようとしましたが、それを完全に理解することはできません。
何か案は?
更新:以下のオラフの答えから離れて、私はこれを思いついた、それはうまくいくようだ
INSERT INTO `timetable`
(`time`, `user_id`, `task_id`, `date`)
(
SELECT
SUM(`time`) AS `time`,
`user_id`,
1 AS `task_id`,
`date`
FROM `timetable` AS `t1`
WHERE `task_id` IN (1,2)
GROUP BY `user_id`, `date`
)
ON DUPLICATE KEY UPDATE `time`=VALUES(`time`);
DELETE FROM `timetable` WHERE `task_id`=2;
誰かがより良い解決策を持っている場合(または私の解決策に知っておくべき落とし穴がある場合)に備えて、質問は開いたままにしておきます
更新2:なぜ以前にこれに気づかなかったのかわかりませんが、ターゲットタスクにのみ存在し、マージする必要のないすべてのエントリも検出されるため、私のソリューションは多くの冗長なINSERTを実行する可能性があります。上記のデータ例では、2番目の行が検出され、再挿入され、重複キーがトリガーされ、時刻が既存の時刻と同じに設定されます。したがって、ターゲットタスクにたとえば10行があり、ソースタスクに0行がある場合でも、10個の(完全に無意味な)INSERTが実行されます。
これは、内部のSELECTすべてをさらに別のSELECTでラップし、COUNT(*)
マージが必要な行のみを検索するために使用することで回避できます。もちろん、これには、マージする必要のない行のtask_idを更新するためにさらにクエリが必要になります(私が知る限り、これを理解するには結合が必要です)。