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

How to delete duplicate rows if they are odd else keep one

这是一款卡牌游戏。 我的 table 是

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 的牌

你还没有说你是哪个 MySQL 版本 运行。此存储过程示例适用于 MySQL 5.6。它运行一个简单的 GROUP BY 查询来获取所有 Number, Player groups with more than 1 card。然后它遍历游标并为返回的每一行运行删除。

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, the ROW_NUMBER() 中为我们提供了 NumberPlayer 分区中的累积计数。 COUNT(*) 为我们提供了 NumberPlayer 分区内的总数。然后,我们可以在所有三个原始列上加入 stats(CTE)和 cards。最后,我们使用 WHERE 子句来决定删除哪些卡片。

在 MySQL < 8.0 中可以采用类似的方法,使用序列变量和连接到另一个派生的 table 以获得每组的计数 -

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));

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