在MySQL / Laravel中同时执行更新并检查重复项

时间:2016-09-27 19:24:38

标签: php mysql sql laravel laravel-4

我正在使用Laravel 4.2并有一个查询:

    DB::table('data')->whereIn('t_id', $new_ids);
                ->whereNotIn('l_id', $old_ids);
                ->groupBy('l_id')->update(array('t_id' => $new_t_id));

这会导致错误:

500 - SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1569648-7302' for key 'data_l_id_t_id_unique' (SQL: update `data` set `t_id` = 7302 where `t_id` in (4772, 4860, 4861, 5653, 6396, 6743) and `l_id` not in (2994190)) @ /

问题似乎是groupBy()子句没有被执行,因此将重复的l_id拉入查询:

mysql> select l_id from data where t_id=7302;
+---------+
| l_id |
+---------+
| 2994190 |
+---------+
1 row in set (0.00 sec)


mysql> select l_id from data where t_id in (4772, 4860, 4861, 5653, 6396, 6743);
+---------+
| l_id |
+---------+
| 1569648 |
| 1593870 |
| 1594096 |
| 1628872 |
| 1569648 |
| 1593870 |
| 1594096 |
| 1628872 |
| 1569648 |
| 1593870 |
| 1594096 |
| 1628872 |
| 1879092 |
| 2283518 |
| 2284586 |
| 2604466 |
+---------+
16 rows in set (0.00 sec)



mysql> select l_id from data where t_id in (4772, 4860, 4861, 5653, 6396, 6743) GROUP BY l_id;
+---------+
| l_id |
+---------+
| 1569648 |
| 1593870 |
| 1594096 |
| 1628872 |
| 1879092 |
| 2283518 |
| 2284586 |
| 2604466 |
+---------+
8 rows in set (0.00 sec)

架构:

mysql> show create table data;

CREATE TABLE `data` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`l_id` bigint(20) unsigned NOT NULL,
`t_id` bigint(20) unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `data_l_id_tag_id_unique` (`l_id`,`t_id`),
KEY `data_t_id_foreign` (`t_id`),
CONSTRAINT `data_l_id_foreign` FOREIGN KEY (`l_id`) REFERENCES `lis` (`id`),
CONSTRAINT `data_t_id_foreign` FOREIGN KEY (`t_id`) REFERENCES `tas` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4544794 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

基本上我需要为某些 l_id 更新 t_id ,但仍然确保没有重复的t_id / l_id发生。我可以通过循环遍历每个t_id并在更新之前检查重复项来做到这一点,但是通过groupBy()认为快捷方式将是更好的方法。

是否可以让Laravel在更新时执行groupBy()?更一般地说,即使在普通的SQL中,也可以在检查重复项时执行更新吗?

编辑:通过

分隔更新

使UPDATE和GROUP BY分离有助于解决GROUP BY问题,但不能解决重复问题:

    $required_l_ids = DB::table('data')->whereIn('t_id', $new_ids);
                ->whereNotIn('l_id', $old_ids);
                ->groupBy('l_id')->lists('l_id');


    if ( !empty($required_l_ids) ) {
            DB::table('data')->whereIn('l_id', $required_l_ids)->whereIn('t_id', $new_ids)->update(array('t_id' => $new_tag_id));
    }

仍然出错:

500 - SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1593870-7302' for key 'data_l_id_t_id_unique' (SQL: update `data` set `t_id` = 7302 where `l_id` in (1593870, 1594096, 1628872, 1879092, 2283518, 2284586, 2604466) and `t_id` in (4772, 4860, 4861, 5653, 6396, 6743)) @ /

编辑2:示例数据

CREATE TABLE `data` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`l_id` bigint(20) unsigned NOT NULL,
`t_id` bigint(20) unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `data_l_id_t_id_unique` (`l_id`,`t_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4544794 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


INSERT into data (l_id, t_id) VALUES (1569648,7302);
INSERT into data (l_id, t_id) VALUES (2994190,7302);
INSERT into data (l_id, t_id) VALUES (1593870,4772);
INSERT into data (l_id, t_id) VALUES (1594096,4772);
INSERT into data (l_id, t_id) VALUES (1628872,4772);
INSERT into data (l_id, t_id) VALUES (1569648,4860);
INSERT into data (l_id, t_id) VALUES (1593870,4860);
INSERT into data (l_id, t_id) VALUES (1594096,4860);
INSERT into data (l_id, t_id) VALUES (1628872,4860);
INSERT into data (l_id, t_id) VALUES (1569648,4861);
INSERT into data (l_id, t_id) VALUES (1593870,4861);
INSERT into data (l_id, t_id) VALUES (1594096,4861);
INSERT into data (l_id, t_id) VALUES (1628872,4861);
INSERT into data (l_id, t_id) VALUES (1879092,5653);
INSERT into data (l_id, t_id) VALUES (2283518,6396);
INSERT into data (l_id, t_id) VALUES (2284586,6396);
INSERT into data (l_id, t_id) VALUES (2604466,6743);


UPDATE data AS d1 LEFT JOIN data AS d2 ON d1.l_id = d2.l_id AND d2.t_id = 7302 SET d1.t_id = 7302 WHERE d1.t_id IN (4772,4860,4861,5653,6396,6743) AND d1.l_id NOT IN (1569648,2994190) AND d2.l_id IS NULL;

sqlfiddle:http://sqlfiddle.com/#!9/e9a50

错误Duplicate entry '1593870-7302' for key 'data_l_id_t_id_unique'

1 个答案:

答案 0 :(得分:1)

我不知道Laravel语法,但我认为这是你想要的MySQL语法:

UPDATE data AS d1
JOIN (SELECT l_id, MIN(t_id) AS min_t_id
      FROM data
      WHERE d1.t_id IN ($new_ids)
      AND d1.l_id NOT IN ($old_ids)
      GROUP BY l_id) AS d3 ON d1.l_id = d3.l_id AND d1.t_id = d3.min_t_id
LEFT JOIN data AS d2 ON d1.l_id = d2.l_id AND d2.t_id = $new_tag_id
SET d1.t_id = $new_tag_id
WHERE d2.l_id IS NULL

这将UPDATEReturn row only if value doesn't exist

中的LEFT JOIN/NULL模式相结合

第一个JOIN确保每个l_id只更新一行,因此您不会创建重复项。它任意选择要替换的最低t_id

DEMO