在插入之前检查值是否存在重复密钥更新时

q5lcpyga  于 2021-06-24  发布在  Mysql
关注(0)|答案(7)|浏览(373)

背景

我有一个应用程序,用户可以提交产品评论。用户还可以编辑以前提交的评论或提交同一产品的另一个评论。
我正在实现一个“自动保存”功能,它每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 需要指向现有的评论,因为理想情况下,这里的索引需要是 product_id , user_idreview_id . 不幸的是,在我的情况下,以下适用:
唯一索引允许对可以包含null的列使用多个null值
虽然有关于上述引用的问题和答案,但我不一定对获取唯一索引中的空值感兴趣,而是想找到一种解决方法。
我想我可以先做一个select查询来检查上面的组合是否存在,如果不存在,继续主查询。但如果可能的话,我想把这些都放到一个查询中。思想?
谢谢。

j5fpnvbx

j5fpnvbx1#

你可以试试做 product_id 以及 user_id 您的主键:

PRIMARY KEY(`product_id`, `user_id`)

并添加评论id作为您的 UNIQUE KEY

n1bvdmb6

n1bvdmb62#

是否尝试在没有审阅id的情况下插入新行?

INSERT INTO review_autosave_data (product_id, user_id, review)
VALUES (50, 1, "lorem ipsum")
ON DUPLICATE KEY
UPDATE review = "lorem ipsum";
zynd9foi

zynd9foi3#

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";
z9smfwbn

z9smfwbn4#

为什么不把问题从db中完全去掉呢?
想必当你的应用程序提交autosave“请求”时,它会得到某种形式的确认,证明它是正确的?您可以发回insert的'id',这样应用程序就可以显式地保存到该记录中—也就是说,第一个autosave将不同于其余的autosave,随后的autosave将只更新特定的autosave记录。
这完全避免了你的数据库层的问题,我想。。。不过,您将有两个查询,一个用于插入新草稿,另一个用于更新特定草稿,不需要特别的键。

wqsoz72f

wqsoz72f5#

只需做第二个表,将举行张贴评论。一旦用户发布评论,就把数据移到那里。
这将消除你的问题,使事情更清楚。您将有一个不应张贴在任何地方的草稿位置和一个将张贴您希望在不同位置显示的审阅的表。您当前的表名也表明它只保存自动保存数据,不保存已发布的数据。

kknvjkwl

kknvjkwl6#

而不是使用 NULL ,使用0表示尚未审阅。
然后改变 DEFAULT NULLNOT NULL .
同时,你也可以摆脱 id 只需要 PRIMARY KEY(product_id, user_id, review_id) 还可以去掉任何作为 PRIMARY KEY .
你为什么要把“评论”当成 BLOB ,当它大概是“文本”时?

nkhmeac6

nkhmeac67#

而不是 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");

优点:你有一个明确的设计,明确的唯一键和外键。
缺点:有两个表包含相似的数据。所以有两个不同的insert语句。如果要合并这两个表(例如,为一个用户显示所有草稿),则需要一个union语句。

相关问题