MySQL如何在不破坏唯一约束或使用占位符行的情况下更新多行

发布于 2025-01-15 04:20:15 字数 2313 浏览 7 评论 0原文

在MySQL中, 我有一个名为 Channel 的表(由 Prisma ORM 自动生成)

CREATE TABLE `Channel` (
    `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
    `guildId` INTEGER UNSIGNED NOT NULL,
    `name` VARCHAR(64) NOT NULL,
    `description` TEXT NULL,
    `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
    `position` INTEGER NOT NULL,
    `parentId` INTEGER NULL,
    `ratelimit` INTEGER NOT NULL DEFAULT 0,
    `type` ENUM('textChannel', 'categoryChannel') NOT NULL,

    INDEX `Channel_guildId_idx`(`guildId`),
    UNIQUE INDEX `Channel_guildId_name_key`(`guildId`, `name`),
    UNIQUE INDEX `Channel_guildId_position_key`(`guildId`, `position`),
    PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

如您所见,有一个 UNIQUE INDEX 约束 Channel_guild_position_key

我怎样才能做一些不破坏这个的事情唯一约束,例如:(我可以使用 SELECT * FROM Channel WHERE guildId = $guildId 获取具有相同 guild_id 的所有行)

更新通道current 的位置字段值到 x(假设 x 大于 current.position) 更新 current.position 和位置 x 之间的所有通道,并将其位置字段减少 1current.position 更新为位置 x

基本上,我想将通道移动到某个位置,并且新位置和旧位置之间的所有内容都需要向前推进。但我被我设置的约束所阻止,该约束使 (position, guildId) 成为唯一标识符,因此需要一个临时值来完成此任务。我正在寻找一种不会打破约束的单操作解决方案

注意:这包括(可能)超过两行,并且单个交换不起作用。

如果您仍然不明白这个问题,请考虑聊天频道,当客户端将频道拖动到不同的位置时,我想更新“之前”和“之后”位置“之间”的所有频道位置。

例子: 这里表格有 3 行

INSERT INTO Channel (id, guildId, name, position, type) VALUES (1, 1, 'ch1', 1, 'textChannel');
INSERT INTO Channel (id, guildId, name, position, type) VALUES (2, 1, 'ch2', 2, 'textChannel');
INSERT INTO Channel (id, guildId, name, position, type) VALUES (3, 1, 'ch3', 3, 'textChannel');

现在,假设我想将通道 'ch3' 移动到位置 1,那么 'ch1' 和 'ch2' 的位置将分别更改为 2、3。 但如果我运行这些操作:(假设我已经获得了 ID)

$guildId = 0

$originalPositionOfChannel3 = 3

$newPosition = 0

UPDATE Channel SET position = $newPosition WHERE guildId = $guildId AND name = 'ch3';
UPDATE Channel SET position = position + 1 WHERE guildId = $guildId AND position < $originalPositionOfChannel3 AND position >= $newPosition;```

In MySQL,
I have a table named Channel (Auto-generated by Prisma ORM)

CREATE TABLE `Channel` (
    `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
    `guildId` INTEGER UNSIGNED NOT NULL,
    `name` VARCHAR(64) NOT NULL,
    `description` TEXT NULL,
    `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
    `position` INTEGER NOT NULL,
    `parentId` INTEGER NULL,
    `ratelimit` INTEGER NOT NULL DEFAULT 0,
    `type` ENUM('textChannel', 'categoryChannel') NOT NULL,

    INDEX `Channel_guildId_idx`(`guildId`),
    UNIQUE INDEX `Channel_guildId_name_key`(`guildId`, `name`),
    UNIQUE INDEX `Channel_guildId_position_key`(`guildId`, `position`),
    PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

As you can see, there's a UNIQUE INDEX constraint Channel_guildId_position_key

How can I do something that doesn't break this unique constraint such that: (I can get all the rows with the same guild_id with SELECT * FROM Channel WHERE guildId = $guildId)

update a channel current's position field value to x (assuming x is bigger than current.position)
update all channels between current.position and position x and decrement their position field by 1
update current.position to position x

Basically, I want to move a channel to a position and everything in between new position and old position needs to advance forward. But I'm getting blocked by the constraint I set which makes (position, guildId) a unique identifier, and thus requiring a temporary value to accomplish this task. I'm looking for a one-operation solution that doesn't break the constraint

Note: This includes (possibly) more than two rows, and a single swap won't work.

If you still do not understand the question, think of chat channels, when client drags a channel to a different position I want to update all the positions of the channels that are "in between" the "before" and "after" positions.

Example:
Here the table has 3 rows

INSERT INTO Channel (id, guildId, name, position, type) VALUES (1, 1, 'ch1', 1, 'textChannel');
INSERT INTO Channel (id, guildId, name, position, type) VALUES (2, 1, 'ch2', 2, 'textChannel');
INSERT INTO Channel (id, guildId, name, position, type) VALUES (3, 1, 'ch3', 3, 'textChannel');

Now, let's say I want to move channel 'ch3' to position 1, then 'ch1' and 'ch2''s position would be changed to 2, 3 respectively.
But if I run these operations: (Assuming I got the IDs already)

$guildId = 0

$originalPositionOfChannel3 = 3

$newPosition = 0

UPDATE Channel SET position = $newPosition WHERE guildId = $guildId AND name = 'ch3';
UPDATE Channel SET position = position + 1 WHERE guildId = $guildId AND position < $originalPositionOfChannel3 AND position >= $newPosition;```

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文