如果它们是奇怪的,如何删除重复的行,否则保留一个

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() 为我们提供了NumberPlayer PARTITION 内的累积计数。 COUNT(*) 为我们提供了NumberPlayer 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));

我绝对不建议使用最后一个示例,至少不要在生产环境中使用。我只是包括在内,因为它可能对某人很有趣。

【讨论】:

以上是关于如果它们是奇怪的,如何删除重复的行,否则保留一个的主要内容,如果未能解决你的问题,请参考以下文章

oracle删除重复的行怎么删啊

根据列子集删除重复项,保留列 E 中具有最高值的行,如果 E 中的值相等,则列 B 中具有最高值的行

有条件地删除重复的pandas python

有条件地删除重复的pandas python

SQL中删除重复的行(重复数据),只保留一行 转

T-SQL:删除所有重复的行但保留一个[重复]