首先,这是我的代码结构和我想要实现的目标:
我有这3张桌子:
+----------------------+
| CONFIG_CAMPAIGN |
+----------------------+
| - id_config |
| // other fields |
+----------------------+
+----------------------+
| SLOT_CONFIG |
+----------------------+
| - id_slot_config |
| - id_config |
| - id_slot_pb |
+----------------------+
+----------------------+
| SLOT_PLACE_BOX |
+----------------------+
| - id_slot_pb |
| - id_place_box |
| - date |
| - hour |
| - slot_available |
+----------------------+
所以一个“slot_place_box”可以有多个“config”,一个“config”可以在多个“slot_place_box”上。我在“slot_config”上有一些触发器来根据配置更新字段“slot_available”。
现在我有一个PHP,我可以获得一些FORM数据并构建一个类似于此的SQL请求(当我遇到问题时):
INSERT INTO media.slot_place_box (id_place_box, date, hour)VALUES (32, '2017-12-10', NULL)ON DUPLICATE KEY UPDATE id_place_box = id_place_box;
SET @id = (SELECT SPB.id_slot_pb FROM media.slot_place_box as SPB WHERE SPB.id_place_box = 32 AND SPB.date = '2017-12-10' AND SPB.hour IS NULL);
INSERT INTO media.slot_config (id_config, id_slot_pb) VALUES (125, @id);
INSERT INTO media.slot_place_box (id_place_box, date, hour)VALUES (32, '2017-12-11', NULL)ON DUPLICATE KEY UPDATE id_place_box = id_place_box;
SET @id = (SELECT SPB.id_slot_pb FROM media.slot_place_box as SPB WHERE SPB.id_place_box = 32 AND SPB.date = '2017-12-11' AND SPB.hour IS NULL);
INSERT INTO media.slot_config (id_config, id_slot_pb) VALUES (125, @id);
INSERT INTO media.slot_place_box (id_place_box, date, hour)VALUES (32, '2017-12-12', NULL)ON DUPLICATE KEY UPDATE id_place_box = id_place_box;
SET @id = (SELECT SPB.id_slot_pb FROM media.slot_place_box as SPB WHERE SPB.id_place_box = 32 AND SPB.date = '2017-12-12' AND SPB.hour IS NULL);
INSERT INTO media.slot_config (id_config, id_slot_pb) VALUES (125, @id);
// and so on
如果根据我之前在我的代码中创建的新配置没有重复,我的想法是创建一个新的“slot_place_box”+“slot_config”
OR
如果我已经有一个“slot_place_box”具有相同的“id_place_box + date + hour”,我只会使用new_config id +我已经拥有的“slot_place_box”插入一个新的“slot_config”。
我选择“INSERT ... ON DUPLICATE KEY /什么都不做/”,因为我需要我INSERT /我已经拥有的行的ID。
我在“slot_place_box”中为字段“id_place_box + date + hour”添加了一个UNIQUE索引键。我的问题开始是因为“小时”可以是一小时(例如“12:00:00”)但也可以是NULL(=对我而言,当NULL表示“日期”的“全天”)。当hour为NULL时,我的UNIQUE索引不起作用,因此INSERT发生了,我在数据库中有重复。
那么如何才能使这个“INSERT ... ON DUPLICATE KEY UPDATE”与具有NULL值的UNIQUE索引一起使用?
我无法使用此解决方案(here),因为我使用的是MySQl 5.6,因此我会尝试触发,因为有人在我的上一个问题(here)中建议我。
我是SQL的初学者,所以我想要一些帮助,请实现此触发器。如果我理解的话,这个想法是用触发器创建一些“虚拟”列,如果“hour”为null则在其中添加一些值+在我的UNIQUE索引中添加此虚拟列,这样ON DUPLICATE KEY就可以了。
所以我的问题是:
答案 0 :(得分:0)
我会自己回答,但如果你们有其他解决方案,我仍然有兴趣听到它。这个解决方案对我有用,因为我正在开发一个新项目,所以它并没有真正影响我已编写的代码。
要解决此问题,我在“slot_place_box”表中添加了一个新列:
+----------------------+
| SLOT_PLACE_BOX |
+----------------------+
| - id_slot_pb |
| - id_place_box |
| - date |
| - hour |
| - slot_available |
| - virtual_hour | <- new field
+----------------------+
这个新字段是VARCHAR(10),等于“小时”,如果不是NULL(例如:“12:00:00”)或默认等于“1”或“小时”为NULL。
然后我在这个表上更改了我的UNIQUE索引“id_place_box + date + virtual_hour”,所以我永远不会得到NULL值。然后我更新我表中已有的数据:
UPDATE slot_place_box SET (virtual_hour = IF(hour is NULL, '1', hour);
// Without the safe UPDATE (I use MySQL WorkBench)
我改变了我的PHP循环以使用新列构建我的请求:
foreach($SPB_data_array as $index => $SPB_data) {
$current_id_pb = $SPB_data['id_pb'];
$current_date = $SPB_data['date'];
if (!empty($SPB_data['hour'])) {
$current_hour = $SPB_data['hour'];
$sql_slot_pb .= 'INSERT INTO media.slot_place_box (id_place_box, date, hour, virtual_hour) VALUES ('. $current_id_pb .', \''. $current_date .'\', \''. $current_hour .'\', \''. $current_hour .'\')
ON DUPLICATE KEY UPDATE id_place_box=id_place_box;
SET @id = (SELECT SPB.id_slot_pb FROM media.slot_place_box as SPB
WHERE SPB.id_place_box = '. $current_id_pb .' AND SPB.date = \''. $current_date .'\' AND SPB.hour = \''. $current_hour .'\');
INSERT INTO media.slot_config (id_config, id_slot_pb) VALUES (:new_config_id, @id);
';
} else {
$sql_slot_pb .= 'INSERT INTO media.slot_place_box (id_place_box, date, hour) VALUES ('. $current_id_pb .', \''. $current_date .'\', NULL)
ON DUPLICATE KEY UPDATE id_place_box=id_place_box;
SET @id = (SELECT SPB.id_slot_pb FROM media.slot_place_box as SPB
WHERE SPB.id_place_box = '. $current_id_pb .' AND SPB.date = \''. $current_date .'\' AND SPB.hour IS NULL);
INSERT INTO media.slot_config (id_config, id_slot_pb) VALUES (:new_config_id, @id);
';
}
}
当“小时”为非空时,我刚添加第四列的“小时”值,默认情况下“virtual_hour”为“1”,因此在这种情况下无需添加。
我不知道它是否是具有大量数据的BIG项目的最佳解决方案,但在我的情况下,这是解决问题的最简单方法。