您还没有说您正在运行哪个 MySQL 版本。此存储过程示例适用于 MySQL 5.6。它运行一个简单的 GROUP BY 查询来获取所有 Number, Player 组超过 1 张卡。然后它遍历游标并对返回的每一行执行删除操作。
CREATE PROCEDURE `sp_DeletePairs`()
BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE _number TINYINT UNSIGNED;
DECLARE _player CHAR(20);
DECLARE _count TINYINT UNSIGNED;
DECLARE `cur` CURSOR FOR
SELECT `Number`, `Player`, COUNT(*) AS `num`
FROM `cards`
GROUP BY `Number`, `Player`
HAVING `num` > 1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO _number, _player, _count;
IF done THEN
LEAVE read_loop;
END IF;
CASE
WHEN _count IN (2, 3) THEN
DELETE FROM `cards` WHERE `Number` = _number AND `Player` = _player LIMIT 2;
WHEN _count = 4 THEN
DELETE FROM `cards` WHERE `Number` = _number AND `Player` = _player LIMIT 4;
END CASE;
END LOOP;
CLOSE cur;
END
显然,如果您愿意,可以将以下 DELETE 查询示例包装在存储过程中。
如果您使用的是 MySQL 8.0 或更高版本,您可以使用 window functions 进行操作 -
WITH `stats` AS (
SELECT `Number`, `Symbol`, `Player`,
ROW_NUMBER() OVER (PARTITION BY `Number`, `Player` ORDER BY `Player`, `Number`, `Symbol`) AS `seq`,
COUNT(*) OVER (PARTITION BY `Number`, `Player`) AS `count_numbers`
FROM cards
)
DELETE `c`
FROM `cards` `c`
INNER JOIN `stats` `s`
ON `c`.`Number` = `s`.`Number`
AND `c`.`Symbol` = `s`.`Symbol`
AND `c`.`Player` = `s`.`Player`
WHERE `s`.`count_numbers` = 4
OR (`s`.`count_numbers` IN (2, 3) AND `s`.`seq` IN (1, 2));
在CTE 内,ROW_NUMBER() 为我们提供了Number、Player PARTITION 内的累积计数。 COUNT(*) 为我们提供了Number、Player PARTITION 内的总数。然后我们可以在所有三个原始列上连接stats(CTE)和cards。最后,我们使用 WHERE 子句来决定要删除哪些卡片。
在 MySQL
DELETE `c`
FROM `cards` `c`
INNER JOIN (
SELECT
`c`.`Number`,
`c`.`Symbol`,
`c`.`Player`,
IF(@prev_number = `c`.`Number` AND @prev_player = `c`.`Player`, @row := @row + 1, @row := 1) AS `seq`,
`counts`.`count_numbers`,
@prev_number := `c`.`Number`,
@prev_player := `c`.`Player`
FROM `cards` `c`
JOIN (SELECT @row := 0, @prev_number := 0, @prev_player:=0) t
INNER JOIN ( SELECT `Player`, `Number`, COUNT(*) AS `count_numbers` FROM `cards` GROUP BY `Player`, `Number`) AS `counts`
ON `c`.`Player` = `counts`.`Player`
AND `c`.`Number` = `counts`.`Number`
ORDER BY `c`.`Player`, `c`.`Number`
) `s`
ON `c`.`Number` = `s`.`Number`
AND `c`.`Symbol` = `s`.`Symbol`
AND `c`.`Player` = `s`.`Player`
WHERE `s`.`count_numbers` = 4
OR (`s`.`count_numbers` IN (2, 3) AND `s`.`seq` IN (1, 2));
我绝对不建议使用最后一个示例,至少不要在生产环境中使用。我只是包括在内,因为它可能对某人很有趣。