Использование Vitess v8 (БД PlanetScale) Таблица:
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;
Данные испытаний:
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');
Первый шаг: (это нормально, если вы не понимаете, почему этот шаг важен)
UPDATE `Channel` SET `position` = 0.5 WHERE `id` = 3;
Отредактированный запрос, предоставленный Ergest:
UPDATE `Channel` AS `ch`
INNER JOIN (
SELECT `id` as `id2`,
( SELECT COUNT(*)
FROM (SELECT * FROM `Channel`) AS `b`
WHERE `b`.`position` <= `a`.`position`
) AS `p`
FROM `Channel` AS `a` WHERE `guildId` = 1
) AS `td` ON `ch`.`id` = `td`.`id2`
SET `ch`.`position` = `td`.`p`;
Ошибка:
error: code = AlreadyExists desc = Duplicate entry '1-2' for key 'Channel.Channel_guildId_position_key' (errno 1062)
Вы используете ORDER BY
.
Документы MySQL:
Multiple-table syntax: For the multiple-table syntax, UPDATE updates rows in each table named in table_references that satisfy the conditions. Each matching row is updated once, even if it matches the conditions multiple times. For multiple-table syntax, ORDER BY and LIMIT cannot be used.
Я не вижу смысла использовать ORDER BY position ASC
внутри подзапроса, если вы не используете LIMIT
Попробуйте использовать INNER JOIN
:
UPDATE `Channel` AS `ch`,
INNER JOIN (
SELECT`id`,
( SELECT COUNT(*)
FROM `Channel` AS `b`
WHERE `b`.`position` <= `a`.`position`
) AS `p`
FROM `Channel` AS `a` WHERE `guildId` = 1
) AS `td` on `ch`.`id` = `td`.`id`;
SET `ch`.`position` = `td`.`p` ;
Когда я удаляю ORDER BY, он дает ту же ошибку... И я даже не использую ORDER BY в UPDATE, это запрос SELECT (теперь я удалил ORDER BY, чтобы предотвратить такие же ответы, как этот)
@AverageCapitalist попробуйте INNER JOIN
и дайте мне знать
Ух ты! Это почти работает! Я понятия не имею, почему это дало мне ошибку... Но там написано "Дублировать запись "1-2"", потому что у меня есть ограничение для уникальной позиции
Когда я вручную выполняю запрос SELECT, он отображает все просто отлично, а ключ «p» не дублируется. Не уверен, почему MySQL считает, что нарушает ограничение уникального индекса.
@AverageCapitalist без данных и описания таблицы не могу сказать
Я думаю, что знаю, почему это происходит... Поскольку MySQL обновляет одну строку за раз, возможно, строка с, например, 0,5 была обновлена до 1, но строка с позицией 1 еще не обновлена, поэтому она сломала ограничение?
@AverageCapitalist, если вы проверите рабочий пример, вы увидите, что пытаетесь обновить 1 2
значения, которые существуют, и именно так UNIQUE INDEX Channel_guildId_position_key(guildId, position)
предполагается работать
Я прочитал ошибку и теперь понимаю почему, но она должна превращать каждую позицию в 1
, 2
, 3
соответственно. Это отлично работает в реализации Postgres, но я использую MySQL... Как мне решить эту проблему? Это не должно нарушать ограничение, если весь запрос UPDATE выполнен
он говорит о неправильном использовании UPDATE и LIMIT, но я даже не использую LIMIT.