合并具有唯一性约束的行

时间:2012-10-29 16:25:12

标签: mysql sql ruby-on-rails-3

我有一个小时间跟踪网络应用程序(在Rails 3.2.8和MySQL中实现)。该应用程序有几个用户在给定日期将时间添加到特定任务。系统设置为每个日期每个任务只能有一次输入(即行)。即如果在同一任务和日期上添加两次时间,它会为现有行添加时间,而不是创建一个新行。 user_id/task_id/date唯一性由UNIQUE索引强制执行。

现在我想要合并2个任务。用最简单的术语来说,将任务ID 2合并到任务ID 1将采用此

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组合将发生冲突。

如果我为每个task_id = 2条目执行插入操作,我想我可以使用唯一约束来执行ON DUPLICATE KEY UPDATE ...。但这看起来非常不优雅。

我还试图找到一种方法来首先使用总计时间更新任务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,因为它还会找到目标中只存在的所有条目任务,不需要合并。在上面的示例数据中,将找到第二行,重新插入,触发on-duplicate-key,并将时间设置为与现有相同。因此,如果目标任务有10行,而源任务有0行,那么它仍会执行10次(完全无意义的)INSERT。

这可以通过将内部SELECT包装在另一个SELECT中来避免,并使用COUNT(*)仅查找需要合并的那些行。当然,这将需要另外的更多查询来更新需要合并的那些行的task_id(据我所知,这需要一个连接来解决)。

1 个答案:

答案 0 :(得分:0)

user_iddate上的总结将是:

select sum(time), user_id, 1, date
from timetable
group by user_id, date;

更新时间表(荣誉为@Flambino):

insert into timetable (time, user_id, task_id, date)
    select sum(time), user_id, 1, date
    from timetable
    group by user_id, date
on duplicate key update time = values(time);

最后删除task_id而不是1:

的所有行
delete from timetable where task_id > 1;

当更新仅限于任务1和2时,请应用@ Flambino更新中的where子句。