Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 如何避免;使用“临时”;在多对多查询中?_Php_Mysql_Query Optimization_Innodb - Fatal编程技术网

Php 如何避免;使用“临时”;在多对多查询中?

Php 如何避免;使用“临时”;在多对多查询中?,php,mysql,query-optimization,innodb,Php,Mysql,Query Optimization,Innodb,这个查询非常简单,我只想按last\u updated字段获取给定类别中的所有文章: SELECT `articles`.* FROM `articles`, `articles_to_categories` WHERE `articles`.`id` = `articles_to_categories`.`article_id` AND `articles_to_categories`.`category_id` = 1 ORDER BY

这个查询非常简单,我只想按
last\u updated
字段获取给定类别中的所有文章:

SELECT
    `articles`.*
FROM
    `articles`,
    `articles_to_categories`
WHERE
        `articles`.`id` = `articles_to_categories`.`article_id`
        AND `articles_to_categories`.`category_id` = 1
ORDER BY `articles`.`last_updated` DESC
LIMIT 0, 20;
但是它跑得很慢。下面是解释所说的:

select_type  table                   type     possible_keys           key         key_len  ref                                rows  Extra
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SIMPLE       articles_to_categories  ref      article_id,category_id  article_id  5        const                              5016  Using where; Using temporary; Using filesort
SIMPLE       articles                eq_ref   PRIMARY                 PRIMARY     4        articles_to_categories.article_id  1
有没有一种方法可以重写这个查询,或者在我的PHP脚本中添加额外的逻辑,以避免使用“临时”;使用文件排序并加快速度

表格结构:

*articles*
id | title | content | last_updated

*articles_to_categories*
article_id | category_id
更新

我最近更新了
索引。我想我的情况可以用d来解释:

在某些情况下,MySQL无法使用 用于解析订单的索引, 尽管它仍然使用索引来查找 与WHERE子句匹配的行。 这些情况包括:

用于获取行的键与ORDER BY中使用的键不同: 从t1中选择*,其中键2=按键1的恒定顺序

您正在加入许多表,并且 ORDER BY中的列不是全部 从第一个非恒定表 用于检索行。(这是 解释输出中的第一个表 没有常量联接类型。)


但我仍然不知道如何修复此问题。

我假设您在数据库中已完成以下操作:

1) articles->id是主键

2) articles\u to\u categories->article\u id是articles->id的外键


3) 您可以在分类id上创建索引。您应该能够通过在文章上添加一个键来避免文件排序。上次更新的文章。MySQL需要按顺序操作的文件排序,但只要按索引列排序(有一些限制),就可以不使用文件排序

有关更多信息,请参见此处:


我应该这样做。正确的计划是使用第一个索引查找前几个记录,并使用第二个索引进行连接。如果它不起作用,请尝试直接连接或其他方法来强制执行正确的索引用法。

这里有一个简化的示例,我在不久前针对一个类似的性能相关问题做了这个示例,它利用了innodb聚集主键索引(显然仅适用于innodb!!)

您有3个表:类别、产品和产品类别,如下所示:

drop table if exists product;
create table product
(
prod_id int unsigned not null auto_increment primary key,
name varchar(255) not null unique
)
engine = innodb; 

drop table if exists category;
create table category
(
cat_id mediumint unsigned not null auto_increment primary key,
name varchar(255) not null unique
)
engine = innodb; 

drop table if exists product_category;
create table product_category
(
cat_id mediumint unsigned not null,
prod_id int unsigned not null,
primary key (cat_id, prod_id) -- **note the clustered composite index** !!
)
engine = innodb;
最重要的是product_catgeory clustered复合主键的顺序,因为这种情况下的典型查询总是由cat_id=x或cat_id in(x,y,z…)引导

我们拥有500K类别、100万产品和1.25亿产品类别

select count(*) from category;
+----------+
| count(*) |
+----------+
|   500000 |
+----------+

select count(*) from product;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+

select count(*) from product_category;
+-----------+
| count(*)  |
+-----------+
| 125611877 |
+-----------+
那么,让我们看看这个模式对于类似于您的查询是如何执行的。所有查询都是冷运行的(在mysql重新启动后),缓冲区为空,没有查询缓存

select
 p.*
from
 product p
inner join product_category pc on 
    pc.cat_id = 4104 and pc.prod_id = p.prod_id
order by
 p.prod_id desc -- sry dont a date field in this sample table - wont make any difference though
limit 20;

+---------+----------------+
| prod_id | name           |
+---------+----------------+
|  993561 | Product 993561 |
|  991215 | Product 991215 |
|  989222 | Product 989222 |
|  986589 | Product 986589 |
|  983593 | Product 983593 |
|  982507 | Product 982507 |
|  981505 | Product 981505 |
|  981320 | Product 981320 |
|  978576 | Product 978576 |
|  973428 | Product 973428 |
|  959384 | Product 959384 |
|  954829 | Product 954829 |
|  953369 | Product 953369 |
|  951891 | Product 951891 |
|  949413 | Product 949413 |
|  947855 | Product 947855 |
|  947080 | Product 947080 |
|  945115 | Product 945115 |
|  943833 | Product 943833 |
|  942309 | Product 942309 |
+---------+----------------+
20 rows in set (0.70 sec) 

explain
select
 p.*
from
 product p
inner join product_category pc on 
    pc.cat_id = 4104 and pc.prod_id = p.prod_id
order by
 p.prod_id desc -- sry dont a date field in this sample table - wont make any diference though
limit 20;

+----+-------------+-------+--------+---------------+---------+---------+------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref           | rows | Extra                                        |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+----------------------------------------------+
|  1 | SIMPLE      | pc    | ref    | PRIMARY       | PRIMARY | 3       | const           |  499 | Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | p     | eq_ref | PRIMARY       | PRIMARY | 4       | vl_db.pc.prod_id |    1 |                                              |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+----------------------------------------------+
2 rows in set (0.00 sec)
所以这是0.70秒,真冷

希望这有帮助:)

编辑

刚刚阅读了您对我上述评论的回复,您似乎有两个选择之一:

create table articles_to_categories
(
article_id int unsigned not null,
category_id mediumint unsigned not null,
primary key(article_id, category_id), -- good for queries that lead with article_id = x
key (category_id)
)
engine=innodb;
或者


取决于您如何定义集群PK的典型查询。

根据解释类别,id已经是一个可能的键。实际上,我最近更新了索引。我不知道为什么不使用索引。也许MySQL希望看到类似(id,last_updated)这样的东西?事实上,您是对的,通过快速查询来删除订单。现在我只需要了解如何让MYSQL使用索引:)我已经尝试创建了(id,last_updated)索引,但MYSQL仍然使用主索引:/@SilverLight-我认为你不能真正摆脱文件排序。。。由于您必须使用
文章到类别.category\u id
上的WHERE子句读取
文章中的行,因此读取顺序由此条件决定。为了摆脱filesort,当按键排序时,MySQL实际上是根据键读取记录,因此结果不需要排序。不确定你的情况下能不能有这个…慢到什么程度?您使用的是什么引擎?@f00查询运行3-5秒,我使用的是innodb(可以在标记中看到)也许可以查看我的示例-重要的是集群PK的顺序。感谢您提供如此详细的答案。我已经按照您的建议创建了一个索引-两个主键现在都是查询中的用户,如您的示例中所示。但,不幸的是,查询仍然需要3秒钟并使用临时表。您的意思是您已将主键从article\u id、category\u id更改为category\u id、article\u id?检查编辑中的“我的类别到文章”表。如果所有其他方法都失败,请发布表定义…然后强制使用它们。但是,由于条件
articles\u to\u categories.category\u id=1
,它可能无法正常工作。对5k行使用临时和文件排序可能是最佳选择。
create table articles_to_categories
(
article_id int unsigned not null,
category_id mediumint unsigned not null,
primary key(article_id, category_id), -- good for queries that lead with article_id = x
key (category_id)
)
engine=innodb;
create table categories_to_articles
(
article_id int unsigned not null,
category_id mediumint unsigned not null,
primary key(category_id, article_id), -- good for queries that lead with category_id = x
key (article_id)
)
engine=innodb;