我有一个用户可以提交产品评论的应用程序。用户还可以编辑以前提交的评论或为同一产品提交另一个评论。
我正在实施一个“自动保存”功能,每隔X秒保存一次表单数据。如果页面意外关闭,用户可以恢复此“草稿”。
这是我桌子的简化版本:
CREATE TABLE `review_autosave_data` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`review_id` int(11) unsigned DEFAULT NULL,
`product_id` int(11) unsigned NOT NULL,
`user_id` int(11) unsigned NOT NULL,
`review` blob,
`name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_index` (`product_id`, `user_id`),
KEY `fk_review_autosave_data_review_id (`review_id`),
KEY `fk_review_autosave_data_product_id (`product_id`),
KEY `fk_review_autosave_data_user_id (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
请记住,此表仅存储草稿 - 而非实际评论。如果我们正在修改评论,review_id
将指向该评论。如果我们正在创建新审核,则此字段将为NULL
。
这是我插入新草稿的查询:
INSERT INTO review_autosave_data (review_id, product_id, user_id, review)
VALUES (25, 50, 1, "lorem ipsum")
ON DUPLICATE KEY
UPDATE review = "lorem ipsum";
这适用于插入新审核草稿。索引会阻止在已存在product_id
和user_id
组合的位置插入新行。
我的问题是插入现有评论的草稿,其中review_id
需要指向现有评论,因为理想情况下,此处的索引必须是{{1}的组合},product_id
和 user_id
。不幸的是,在我的情况下,以下适用:
UNIQUE索引允许可以包含NULL的列的多个NULL值
虽然有关于上述引用的问题和答案,但我并不一定对将null值作为唯一索引的一部分感兴趣 - 而是找到解决方法。
我想我可以先做一个select查询来检查上面的组合是否存在,如果没有继续主查询。但是如果可能的话,我想把所有这些都集成到一个查询中。想法?
感谢。
答案 0 :(得分:3)
您可以创建两个表格,例如review_autosave_data
和review_insert_drafts
(一个用于新评论,一个用于评论更新),而不是review_update_drafts
。
CREATE TABLE `review_insert_drafts` (
`product_id` int(11) unsigned NOT NULL,
`user_id` int(11) unsigned NOT NULL,
`review` blob,
`name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`product_id`, `user_id`),
CONSTRAINT FOREIGN KEY (`product_id`) REFERENCES `products` (`id`),
CONSTRAINT FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
);
CREATE TABLE `review_update_drafts` (
`review_id` int(11) unsigned NOT NULL,
`review` blob,
`name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`review_id`),
CONSTRAINT FOREIGN KEY (`review_id`) REFERENCES `reviews` (`id`)
);
(不确定name
列的适用对象。)
在您的应用程序中,您必须检查用户是在撰写新评论还是正在更新现有评论。
对于您要运行的新评论:
INSERT INTO review_insert_drafts (product_id, user_id, review)
VALUES (50, 1, "lorem ipsum")
ON DUPLICATE KEY
UPDATE review = "lorem ipsum";
或
REPLACE INTO review_insert_drafts (product_id, user_id, review)
VALUES (50, 1, "lorem ipsum");
对于您运行的评论更新:
INSERT INTO review_update_drafts (review_id, review)
VALUES (25, "lorem ipsum")
ON DUPLICATE KEY
UPDATE review = "lorem ipsum";
或
REPLACE INTO review_update_drafts (review_id, review)
VALUES (25, "lorem ipsum");
优点:您的设计清晰,具有清晰的唯一键和外键。
缺点:您有两个包含类似数据的表。所以你有两个不同的插入语句。如果要组合两个表(例如,显示用户的所有草稿),则需要UNION语句。
答案 1 :(得分:2)
不使用compare = 'LIKE'
,而是使用0表示尚未进行审核。
然后将NULL
更改为DEFAULT NULL
。
与此同时,你也可以摆脱NOT NULL
而只是id
还要删除任何作为PRIMARY KEY(product_id, user_id, review_id)
。
为什么你有"评论"如PRIMARY KEY
,大概是" TEXT"?
答案 2 :(得分:1)
只需制作第二张表即可保留已发布的评论。一旦用户发布评论,就会在那里移动数据。
这将消除您的问题并使事情更清晰。 您将拥有一个不应在任何地方发布的草稿的位置,以及一个将要在各个位置显示的已发布评论的表格。您当前的表名也表明它只保留自动保存数据而没有发布数据。
答案 3 :(得分:1)
INSERT INTO review_autosave_data (review_id, product_id, user_id, review)
SELECT 25, 50, 1, "lorem ipsum"
WHERE NOT EXISTS
(SELECT review_id FROM review_autosave_data
WHERE product_id=50 AND user_id = 1 AND review_id IS NULL)
ON DUPLICATE KEY
UPDATE review = "lorem ipsum";
答案 4 :(得分:1)
您是否尝试插入没有review_id的新行?
INSERT INTO review_autosave_data (product_id, user_id, review)
VALUES (50, 1, "lorem ipsum")
ON DUPLICATE KEY
UPDATE review = "lorem ipsum";
答案 5 :(得分:1)
您可以尝试将product_id
和user_id
设为主键:
PRIMARY KEY(`product_id`, `user_id`)
并将review_id添加为UNIQUE KEY
答案 6 :(得分:1)
为什么不完全从DB中移出问题?
大概是当您的应用程序提交自动保存请求时#39;它会得到某种确认,它发生了吗?您可以发回“ID'然后,应用程序可以显式保存到该记录中 - 也就是说,第一次自动保存将不同,然后其余的和随后的自动保存将只更新特定的自动保存记录。
这完全避免了数据库层中的问题我认为......你将有两个查询,一个用于插入新草稿,另一个用于更新特定草稿,不需要花哨的密钥。