从SELECT中插入,使列对唯一

时间:2017-08-12 23:52:06

标签: php mysql unique-constraint

我有一张桌子(例如这里简化):

CREATE TABLE msg
(
    msg_id VARCHAR(30) NOT NULL,
    user_id BIGINT NOT NULL,
    message TEXT,

    UNIQUE INDEX msg_index (msg_id, user_id);
);

我允许用户使用简单的INSERT INTO添加新消息,如下所示:

INSERT INTO msg (msg_id, user_id, message)
       VALUES ($_POST["id"], $user["id"], $_POST["message"]);

一旦有消息,我允许用户创建 Duplicate 。声明非常简单,我可以这样做:

INSERT INTO msg (msg_id, user_id, message)
       SELECT CONCAT(msg_id, "_Copy") AS msg_id, user_id, message
          WHERE msg_id = $_POST["id"];

第一次工作正常,只有当用户在同一条消息上再次尝试 Duplicate 命令时,才会因UNIQUE INDEX约束而失败。

有没有办法更改CONTACT()语句以生成唯一标识符(即“_Copy1”,“_ Copy2”,...)或者我是否必须测试INSERT是否成功如果没有,请再次尝试下一个可能的条目? (我知道如何做到这一点,我只是想知道MySQL是否提供了令人难以置信的功能,例如自动创建一组独特的列,就像魔术一样!)

3 个答案:

答案 0 :(得分:1)

你总是可以使用RAND()函数来创建随机数(你可以搜索关于如何在MySQL中生成随机字符串的SO),但是我真的不认为它是#sa;好的解决方案。

您的ID应该是唯一的,并且可能会自动递增 添加一个新列可能会好得多,也许" source_msg"这将保存原始邮件的ID(这样您就可以始终知道哪个是用户复制当前邮件的原始邮件)。

如果是"原创"消息 - 只要放在那里0。

表格结构:

CREATE TABLE msg
(
    msg_id int NOT NULL AUTO_INCREMENT,
    source_msg INT NOT NULL,
    user_id BIGINT NOT NULL,
    message TEXT,
    PRIMARY KEY (ID),
    UNIQUE INDEX msg_index (msg_id, user_id);
);

新消息:

INSERT INTO msg (msg_id, source_msg, user_id, message)
    VALUES (NULL, 0, $user["id"], $_POST["message"]);

重复留言:

INSERT INTO msg (msg_id, source_msg, user_id, message)
    SELECT null, msg_id, user_id, message;
  

无论如何 - 阅读有关boby table的信息。

答案 1 :(得分:1)

你可以,但它暴露在竞争条件下。因此,它几乎可以一直工作 - 但不是在重载下:

INSERT INTO msg (msg_id, user_id, message)
    SELECT (CASE WHEN COUNT(*) = 0 THEN v_msg_id
                 ELSE CONCAT_WS('_', SUBSTRING_INDEX(v_msg_id, '_', 1), COUNT(*) + 1) AS msg_id,
           v_user_id, v_message
    FROM msg
    WHERE msg_id = v_msg_id;

注意:这假设msg_id没有下划线。如果可能的话,很容易调整逻辑。

更安全的替代方案是将UUID附加到最​​后。然而,这非常难看。

答案 2 :(得分:1)

由于“msg_id”是一个字符串,我可能会尝试使用时间戳(以毫秒为单位),因此您将获得一个盒子时间戳,性能(不需要搜索所有以前的副本)和“或多或少”的唯一性如果超过每1秒1000个邮件副本的速度,则会被违反。

INSERT INTO msg (msg_id, user_id, message)
       SELECT CONCAT(msg_id, '_', ROUND(UNIX_TIMESTAMP(CURTIME(4)) * 1000)) AS msg_id,
              user_id,
              message
       WHERE msg_id = $_POST['id'];

注意

由于可能的SQL注入,此语法msg_id = $_POST['id'];非常容易受到攻击。我建议使用占位符,不要直接在查询中传递输入。