MySQL在多个表上共享密钥

时间:2013-11-30 20:24:05

标签: mysql sql optimization key

我正在尝试优化我的查询,我认为我需要将一个组合密钥添加到位于三个不同表中的两个date字段。

表格:

playerjoinsplayerleaves

+------------+------------+--------+-------+-----------+------------------+
|  "Field"   |   "Type"   | "Null" | "Key" | "Default" |     "Extra"      |
+------------+------------+--------+-------+-----------+------------------+
| "id"       | "int(11)"  | "NO"   | "PRI" | \N        | "auto_increment" |
| "globalId" | "int(11)"  | "NO"   | "MUL" | \N        | ""               |
| "date"     | "datetime" | "NO"   | "MUL" | \N        | ""               |
| "serverId" | "int(11)"  | "NO"   | "MUL" | \N        | ""               |
| "playerId" | "int(11)"  | "NO"   | "MUL" | \N        | ""               |
+------------+------------+--------+-------+-----------+------------------+

levelsloaded

+--------------+------------+--------+-------+-----------+------------------+
|   "Field"    |   "Type"   | "Null" | "Key" | "Default" |     "Extra"      |
+--------------+------------+--------+-------+-----------+------------------+
| "id"         | "int(11)"  | "NO"   | "PRI" | \N        | "auto_increment" |
| "globalId"   | "int(11)"  | "NO"   | "MUL" | \N        | ""               |
| "date"       | "datetime" | "NO"   | "MUL" | \N        | ""               |
| "serverId"   | "int(11)"  | "NO"   | "MUL" | \N        | ""               |
| "gamemodeId" | "int(11)"  | "NO"   | "MUL" | \N        | ""               |
| "mapId"      | "int(11)"  | "NO"   | "MUL" | \N        | ""               |
+--------------+------------+--------+-------+-----------+------------------+

MySQL查询:

SELECT llbelow.id, llbelow.globalId, llbelow.date, llbelow.serverId, llbelow.gamemodeId, llbelow.mapId
FROM (
    SELECT ll.id, ll.globalId, ll.date, ll.serverId, ll.gamemodeId, ll.mapId, pjl.origin, pjl.date AS pjldate 
    FROM 
    (
        (SELECT id, globalId, date, serverId, playerId, 'playerjoins' AS origin
        FROM playerjoins pj WHERE playerId = 2224)
    UNION ALL
        (SELECT id, globalId, date, serverId, playerId, 'playerleaves' AS origin
        FROM playerleaves pl WHERE playerId = 2224)
    )
    pjl
    JOIN levelsloaded ll
    ON pjl.date <= ll.date
) llbelow
LEFT OUTER JOIN (
    SELECT ll.id, ll.globalId, pjl.date AS pjldate  
    FROM 
    (
        (SELECT id, globalId, date, serverId, playerId, 'playerjoins' AS origin
        FROM playerjoins pj WHERE playerId = 2224)
    UNION ALL
        (SELECT id, globalId, date, serverId, playerId, 'playerleaves' AS origin
        FROM playerleaves pl WHERE playerId = 2224)
    )
    pjl
    JOIN levelsloaded ll
    ON pjl.date <= ll.date
) llbelow_inner
ON llbelow.id = llbelow_inner.id AND (llbelow.pjldate < llbelow_inner.pjldate OR (llbelow.pjldate = llbelow_inner.pjldate AND llbelow.globalId < llbelow_inner.globalId))
WHERE llbelow_inner.id IS NULL AND origin = 'playerjoins'
ORDER BY llbelow.date DESC, llbelow.pjldate DESC

查询的DESCRIBE

+------+----------------+--------------+--------+-----------------+------------+-----------+-------+---------+------------+------------------------------------------------+
| "id" | "select_type"  |   "table"    | "type" | "possible_keys" |   "key"    | "key_len" | "ref" | "rows"  | "filtered" |                    "Extra"                     |
+------+----------------+--------------+--------+-----------------+------------+-----------+-------+---------+------------+------------------------------------------------+
| "1"  | "PRIMARY"      | "<derived2>" | "ALL"  | \N              | \N         | \N        | \N    | "74494" | "100,00"   | "Using where; Using temporary; Using filesort" |
| "1"  | "PRIMARY"      | "<derived5>" | "ALL"  | \N              | \N         | \N        | \N    | "74494" | "100,00"   | "Using where; Not exists"                      |
| "5"  | "DERIVED"      | "<derived6>" | "ALL"  | \N              | \N         | \N        | \N    | "92"    | "100,00"   | ""                                             |
| "5"  | "DERIVED"      | "ll"         | "ALL"  | "date"          | \N         | \N        | \N    | "1578"  | "100,00"   | "Using where; Using join buffer"               |
| "6"  | "DERIVED"      | "pj"         | "ref"  | "playerId"      | "playerId" | "4"       | ""    | "52"    | "100,00"   | ""                                             |
| "7"  | "UNION"        | "pl"         | "ref"  | "playerId"      | "playerId" | "4"       | ""    | "40"    | "100,00"   | ""                                             |
| \N   | "UNION RESULT" | "<union6,7>" | "ALL"  | \N              | \N         | \N        | \N    | \N      | \N         | ""                                             |
| "2"  | "DERIVED"      | "<derived3>" | "ALL"  | \N              | \N         | \N        | \N    | "92"    | "100,00"   | ""                                             |
| "2"  | "DERIVED"      | "ll"         | "ALL"  | "date"          | \N         | \N        | \N    | "1578"  | "100,00"   | "Using where; Using join buffer"               |
| "3"  | "DERIVED"      | "pj"         | "ref"  | "playerId"      | "playerId" | "4"       | ""    | "52"    | "100,00"   | ""                                             |
| "4"  | "UNION"        | "pl"         | "ref"  | "playerId"      | "playerId" | "4"       | ""    | "40"    | "100,00"   | ""                                             |
| \N   | "UNION RESULT" | "<union3,4>" | "ALL"  | \N              | \N         | \N        | \N    | \N      | \N         | ""                                             |
+------+----------------+--------------+--------+-----------------+------------+-----------+-------+---------+------------+------------------------------------------------+

我需要做些什么才能优化查询?

编辑:添加了http://sqlfiddle.com/#!2/eb702/2示例数据。我想要的是以下内容,对于重建查询感兴趣的人,非正式地说:

  • 此数据库存储来自FPS射击游戏的日志文件中的信息。
  • 玩家可以加入或离开服务器。
  • 服务器平均每15-30分钟加载一张地图。
  • 我想在关于玩家x的个人日志中只显示他实际玩的地图。

更正式地说:我希望所有来自levelsloaded的行,加载级别之前玩家最后一次操作(加入/离开)加入服务器。
(注意:这只是形式化查询的一种方式,没有必要坚持下去)

我想要的SQLFiddle输出数据:http://sqlfiddle.com/#!2/eb702/1

另外,重要的是,查询需要快速。它应该能够实时使用,所以我的目标是在数据库大小不变的情况下,响应时间不到100毫秒。
正如您所看到的,我确实使用了一个工作查询来提供我想要的输出数据,但是该查询在实时数据库上运行96秒,如果我摆脱playerjoinsplayerleaves之间的联合,甚至还需要2秒。 {{1}}。

1 个答案:

答案 0 :(得分:0)

我建议您合并表格playerjoinsplayerleaves

CREATE TABLE `playeraction` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `globalId` int(11) NOT NULL DEFAULT 1,
  `date` datetime NOT NULL,
  `serverId` int(11) NOT NULL,
  `playerId` int(11) NOT NULL,
  `action` char(1) NOT NULL DEFAULT 'J',
  PRIMARY KEY (`id`),
  KEY `globalId` (`globalId`),
  KEY `date` (`date`),
  KEY `serverId` (`serverId`),
  KEY `playerId` (`playerId`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;

然后替换:

    SELECT id, globalId, date, serverId, playerId, 'playerjoins' AS origin
    FROM playerjoins pj WHERE playerId = 1)
UNION ALL
    (SELECT id, globalId, date, serverId, playerId, 'playerleaves' AS origin
    FROM playerleaves pl WHERE playerId = 1)

    SELECT id, globalId, date, serverId, playerId, action FROM playeraction

现在你的查询要短得多:

SELECT llbelow.id, llbelow.globalId, llbelow.date, llbelow.serverId,
       llbelow.gamemodeId, llbelow.mapId
FROM (
    SELECT ll.id, ll.globalId, ll.date, ll.serverId, ll.gamemodeId,
           ll.mapId, pjl.action, pjl.date AS pjldate 
      FROM playeraction pjl
      JOIN levelsloaded ll ON pjl.date <= ll.date
     WHERE playerId = 2224
) llbelow
LEFT OUTER JOIN (
    SELECT ll.id, ll.globalId, pjl.date AS pjldate  
      FROM playeraction pjl
      JOIN levelsloaded ll ON pjl.date <= ll.date
     WHERE playerId = 2224
) llbelow_inner
ON llbelow.id = llbelow_inner.id
   AND (llbelow.pjldate < llbelow_inner.pjldate
        OR (llbelow.pjldate = llbelow_inner.pjldate
            AND llbelow.globalId < llbelow_inner.globalId))
WHERE llbelow_inner.id IS NULL AND llbelow.action = 'J'
ORDER BY llbelow.date DESC, llbelow.pjldate DESC

如有必要,我们可以更多地简化它。