我可以优化这种查询吗?

Posted

技术标签:

【中文标题】我可以优化这种查询吗?【英文标题】:Can I optimize this kind of query? 【发布时间】:2012-04-24 14:31:50 【问题描述】:

我有这个问题:

SELECT 
    s.last_spread, s.sd, s.mean, s.id
    ,c.id_ticker, c.coef
    ,t.ticker
    ,p.last, p.price

FROM (SELECT * FROM spreads WHERE spreads.id_check=1 LIMIT 100,500 ) as s 

    INNER JOIN coef as c 
        ON c.id_spread = s.id

    INNER JOIN tickers AS t
            ON t.id = c.id_ticker

    LEFT JOIN (SELECT prices.id_ticker, MAX(prices.date) as last, prices.price FROM prices GROUP BY prices.id_ticker) AS p
            ON p.id_ticker = t.id

这些是表的架构:

mysql> desc spreads;
+-------------+---------+------+-----+---------+----------------+
| Field       | Type    | Null | Key | Default | Extra          |
+-------------+---------+------+-----+---------+----------------+
| id          | int(11) | NO   | PRI | NULL    | auto_increment |
| id_check    | int(11) | YES  | MUL | NULL    |                |
| sd          | double  | YES  |     | NULL    |                |
| mean        | double  | YES  |     | NULL    |                |
| last_spread | double  | YES  |     | NULL    |                |
+-------------+---------+------+-----+---------+----------------+
5 rows in set (0.00 sec)


mysql> desc coef;
+-----------+---------+------+-----+---------+----------------+
| Field     | Type    | Null | Key | Default | Extra          |
+-----------+---------+------+-----+---------+----------------+
| id        | int(11) | NO   | PRI | NULL    | auto_increment |
| id_spread | int(11) | YES  | MUL | NULL    |                |
| id_ticker | int(11) | YES  |     | NULL    |                |
| coef      | double  | YES  |     | NULL    |                |
| side      | double  | YES  |     | NULL    |                |
+-----------+---------+------+-----+---------+----------------+
5 rows in set (0.00 sec)


mysql> desc tickers;
+----------+------------------+------+-----+---------+----------------+
| Field    | Type             | Null | Key | Default | Extra          |
+----------+------------------+------+-----+---------+----------------+
| id       | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| ticker   | varchar(45)      | NO   |     | NULL    |                |
| name     | varchar(150)     | NO   |     | NULL    |                |
| category | varchar(150)     | NO   |     | NULL    |                |
| issuer   | varchar(150)     | NO   |     | NULL    |                |
+----------+------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)


mysql> desc prices;
+-----------+------------------+------+-----+---------+----------------+
| Field     | Type             | Null | Key | Default | Extra          |
+-----------+------------------+------+-----+---------+----------------+
| id        | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| id_ticker | int(10) unsigned | NO   | MUL | NULL    |                |
| date      | date             | NO   |     | NULL    |                |
| price     | double           | NO   |     | NULL    |                |
+-----------+------------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)

这些是以上表格的索引;

mysql> show indexes from spreads;
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table   | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| spreads |          0 | PRIMARY   |            1 | id          | A         |        2299 |     NULL | NULL   |      | BTREE      |         |
| spreads |          1 | check_idx |            1 | id_check    | A         |           1 |     NULL | NULL   | YES  | BTREE      |         |
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.00 sec)


mysql> show indexes from coef;
+-------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name          | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| coef  |          0 | PRIMARY           |            1 | id          | A         |        9078 |     NULL | NULL   |      | BTREE      |         |
| coef  |          1 | spread_ticker_idx |            1 | id_spread   | A         |        NULL |     NULL | NULL   | YES  | BTREE      |         |
| coef  |          1 | spread_ticker_idx |            2 | id_ticker   | A         |        NULL |     NULL | NULL   | YES  | BTREE      |         |
+-------+------------+-------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
3 rows in set (0.00 sec)


mysql> show indexes from tickers;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| tickers |          0 | PRIMARY  |            1 | id          | A         |         100 |     NULL | NULL   |      | BTREE      |         |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
1 row in set (0.00 sec)


mysql> show indexes from prices;
+--------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table  | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| prices |          0 | PRIMARY   |            1 | id          | A         |       19962 |     NULL | NULL   |      | BTREE      |         |
| prices |          1 | id_ticker |            1 | id_ticker   | A         |       19962 |     NULL | NULL   |      | BTREE      |         |
+--------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.15 sec)

这是查询的解释:

+----+-------------+------------+--------+-------------------+-------------------+---------+---------------------------+--------+-------------+
| id | select_type | table      | type   | possible_keys     | key               | key_len | ref                       | rows   | Extra       |
+----+-------------+------------+--------+-------------------+-------------------+---------+---------------------------+--------+-------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL              | NULL              | NULL    | NULL                      |    500 |             |
|  1 | PRIMARY     | c          | ref    | spread_ticker_idx | spread_ticker_idx | 5       | s.id                      |     90 | Using where |
|  1 | PRIMARY     | t          | eq_ref | PRIMARY           | PRIMARY           | 4       | spreadtrading.c.id_ticker |      1 | Using where |
|  1 | PRIMARY     | <derived3> | ALL    | NULL              | NULL              | NULL    | NULL                      |    100 |             |
|  3 | DERIVED     | prices     | index  | NULL              | id_ticker         | 4       | NULL                      | 119774 |             |
|  2 | DERIVED     | spreads    | ref    | check_idx         | check_idx         | 5       |                           |   2298 | Using where |
+----+-------------+------------+--------+-------------------+-------------------+---------+---------------------------+--------+-------------+
6 rows in set (0.27 sec)

我可以优化它吗?

谢谢!

编辑:

我想知道索引和表的结构是否针对我上面发布的查询进行了优化。我使用此查询得到的结果很好,效果很好,但也许我可以对其进行优化以提高查询的“速度”。

【问题讨论】:

哇。你怎么认为?你试过什么吗? EXPLAIN 输出中的某些表缺少选定的key 是否不会引发标志?目前查询需要多长时间?你真的认为你可以把所有这些信息都扔掉并得到答案吗? 您对价格的派生查询是最大的罪魁祸首。不要在那里使用派生查询。 @eggyal - 好吧,如果 op 刚刚发布了他的查询,那么 我们 会要求他发布表结构和计划,所以我认为这是这些帖子的改进非常受欢迎 @Lamak:是的。我不想阻止发布相关信息,而只是呈现它而不试图分析其内容......? @Dail - 我希望你知道,如果没有 ORDER BY 在那个 spreads 子查询中,你可能不会得到你期望的行; SQL 本质上是无序的,并且(尤其是WHERE 子句)大多数实现都有优化器来调整语句以高效运行,这可能会修改结果排序。因此,您目前正在获得“随机”行。 【参考方案1】:

我认为您可以通过删除spreads 子查询并将WHERE 子句移到末尾来获得一些好处,如下面的代码所示。这将失去您的 LIMIT 限制 - 也许您也可以在末尾添加 LIMIT 子句,具体取决于您在限制输出大小方面要达到的目标。

SELECT 
    s.last_spread, s.sd, s.mean, s.id
    ,c.id_ticker, c.coef
    ,t.ticker
    ,p.last, p.price

FROM  spreads as s 

    INNER JOIN coef as c 
        ON c.id_spread = s.id

    INNER JOIN tickers AS t
            ON t.id = c.id_ticker

    LEFT JOIN (SELECT prices.id_ticker, MAX(prices.date) as last, prices.price FROM prices GROUP BY prices.id_ticker) AS p
            ON p.id_ticker = t.id
WHERE s.id_check = 1

【讨论】:

我之前没有注意到这一点,但是prices.price 既没有分组,也没有包含在聚合函数中——这意味着它返回了一个“随机”行——你有什么建议可以解决这个问题吗?

以上是关于我可以优化这种查询吗?的主要内容,如果未能解决你的问题,请参考以下文章

Mysql子查询优化?

会优化,你真的会优化吗?其实你可能真的缺少一份理解数据库篇

查询成本是 MySQL 查询优化的最佳指标吗?

这个查询看起来优化了吗?

查询/数据库优化:如何优化? (我应该使用物化视图吗?)

面试:做过sql优化吗?