1

我有这张桌子用于 2-4 名玩家的纸牌游戏:

CREATE TABLE IF NOT EXISTS `cardgame` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `players` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `p1` mediumint(6) unsigned NOT NULL DEFAULT '0',
  `p2` mediumint(6) unsigned NOT NULL DEFAULT '0',
  `p3` mediumint(6) unsigned NOT NULL DEFAULT '0',
  `p4` mediumint(6) unsigned NOT NULL DEFAULT '0',
  `p1_state` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `p2_state` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `p3_state` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `p4_state` tinyint(1) unsigned NOT NULL DEFAULT '0',
  /* other rows */,
  `game_state` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=79218 DEFAULT CHARSET=utf8;

我必须检查所有比赛,我是一名球员。(在示例中我的 user_id 是 1981)

mysql> EXPLAIN SELECT id FROM cardgame 
WHERE ((p1=1981 AND p1_state=1) OR (p2=1981 AND p2_state=1) 
OR (p3=1981 AND p3_state=1) OR (p4=1981 AND p4_state=1)) 
AND game_state < 7;
+----+-------------+----------+------+---------------+------+---------+------+-------+-------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows  | Extra       |
+----+-------------+----------+------+---------------+------+---------+------+-------+-------------+
|  1 | SIMPLE      | cardgame | ALL  | NULL          | NULL | NULL    | NULL | 79208 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+-------+-------------+

我可以在哪些行上创建索引?p1, p1_state, p2.. p3.. p4, p4_state 行需要索引加上 game_state,但不是太多了吗?

或者我必须以某种方式重新设计架构?

4

2 回答 2

1

或者我必须以某种方式重新设计架构?

是的,标准化您的架构:

CREATE TABLE game_players (
  game_id  MEDIUMINT NOT NULL,
  position TINYINT   NOT NULL,
  player   MEDIUMINT NOT NULL,
  state    TINYINT UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (game_id, position),
  UNIQUE (game_id, player), -- if a player can only be in each game once
  INDEX (player, state)
)
SELECT id, 1, p1, p1_state FROM cardgame
  UNION ALL
SELECT id, 2, p2, p2_state FROM cardgame
  UNION ALL
SELECT id, 3, p3, p3_state FROM cardgame
  UNION ALL
SELECT id, 4, p4, p4_state FROM cardgame;

ALTER TABLE cardgame 
  DROP p1, DROP p1_state,
  DROP p2, DROP p2_state,
  DROP p3, DROP p3_state,
  DROP p4, DROP p4_state;

然后您的查询变为:

SELECT g.id
FROM   game_players p JOIN cardgame g ON p.game_id = g.id
WHERE  p.player = 1981 AND p.state = 1
   AND g.game_state < 7;
于 2013-02-09T10:27:42.217 回答
0

关于数据正确性的说明:

@eggyal 在另一个答案中的重新设计删除了DEFAULT您列中的大部分 s。这是好事。

a 的一大好处NOT NULL是防止您的程序创建没有正确指定所有列的行。有一个DEFAULT无用的渲染。让我们看一个类似于您的纸牌游戏桌的桌子:

CREATE TABLE IF NOT EXISTS `cardgame` (
    `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    `player_names` varchar(20) NOT NULL,
    `players` tinyint(1) unsigned NOT NULL DEFAULT '0'
);

所以现在你创建一个这样的行:

INSERT INTO cardgame (player_names) VALUES ('Bob and Doug');

您没有指定 ,id因为它是AUTO_INCREMENT,并且您指定player_names正确,但是糟糕,您忘记指定players. 现在,您的players列中的值为 0,这是错误数据。从来没有这样的情况,你应该能够在不指定玩家数量的情况下向表中添加一行,对吧?这DEFAULT '0'消除了该限制,更糟糕的是,将无效数量的玩家放入列中。有 0 个玩家的游戏永远都不正确,对吧?但是您DEFAULT在其中放置了无效数量的玩家。即使您将 更改为DEFAULT像 2 这样的有效数字,您也不知道它是正确的。您必须始终指定玩家人数,对吗?DB无法知道。

因此,永远不要指定 a DEFAULT,除非它专门是一个不必每次都指定的列。

于 2013-02-09T16:03:11.853 回答