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列のほとんどが削除されます。それはいい。

a の大きな利点の 1 つは、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。これで、列の値が 0 になりましたplayers。これは不良データです。プレイヤーの数を指定せずにテーブルに行を追加できる必要がある場合はありませんよね? これDEFAULT '0'により、その制限が取り除かれ、さらに悪いことに、列に無効な数のプレーヤーが配置されます。プレイヤーが0人でゲームをすることは決して正しくありませんよね? しかしDEFAULT、そこに無効な数のプレーヤーを入れました。を 2 のような有効な数値に変更してもDEFAULT、それが正しいかどうかはわかりません。常にプレイヤーの数を指定する必要がありますよね? DBは知ることができません。

したがって、DEFAULT特に毎回指定する必要のない列でない限り、a を指定しないでください。

于 2013-02-09T16:03:11.853 に答える