如果它们是奇怪的,如何删除重复的行,否则保留一个
Posted
技术标签:
【中文标题】如果它们是奇怪的,如何删除重复的行,否则保留一个【英文标题】:How to delete duplicate rows if they are odd else keep one 【发布时间】:2022-01-18 14:49:51 【问题描述】:这是一款卡牌游戏。 我的桌子是
Number | Symbol | Player |
---|---|---|
1 | C | F |
1 | S | F |
1 | D | F |
1 | H | F |
2 | S | F |
2 | C | F |
2 | D | F |
3 | H | F |
2 | H | S |
3 | S | S |
我正在尝试从玩家 F 中移除所有 1 张牌,因为他有偶数张牌,并且他已经收集了所有牌。 而且我只想从玩家 F 中删除 2 行的 2 张牌,因为他有奇数个,最后 2 行在 S 玩家上
我正在尝试为此创建一个程序,我只设法保留一排播放器
PROCEDURE `deleteDupl`()
BEGIN
DELETE c1 FROM cards c1, cards c2 WHERE c1.Symbol > c2.Symbol AND c1.Number = c2.Number AND c1.Player = c2.Player;
END
--编辑 游戏的重点是从你的对手那里挑选牌,一旦你有 2 张相同的牌(牌的数量而不是符号),你就丢掉它们(不管是什么符号,随机丢 2 个相同的数字)
但在游戏开始时,您可能会得到超过 2 张相同的牌,例如 F 玩家拥有所有 A,因此他必须将它们全部丢弃
或者像 F 玩家有三倍于 2 的牌,他必须丢掉两张牌(无论是什么符号),直到他从对手那里挑选出编号为 2 的牌
【问题讨论】:
显示所需的最终数据状态。在源数据中添加备注列,并为每一行添加简短的解释,例如“必须删除,因为 ...”或“必须保存,因为 ...”。 我只想从玩家 F 的 2 张牌中删除 2 行 总共 3 张中的哪 2 张?为什么肯定是他们? 如果 2 名玩家每人有 2 张牌对应某个号码 - 是否必须删除所有这些行(因为每个玩家的牌数都是偶数)? 【参考方案1】:您还没有说您正在运行哪个 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));
我绝对不建议使用最后一个示例,至少不要在生产环境中使用。我只是包括在内,因为它可能对某人很有趣。
【讨论】:
以上是关于如果它们是奇怪的,如何删除重复的行,否则保留一个的主要内容,如果未能解决你的问题,请参考以下文章