查询中的查询速度非常慢。这有什么问题? mySQL
Incredibly slow query inside query. What is wrong with this? mySQL
SELECT *
FROM grants
INNER JOIN people on grants.volID=people.vol_id
INNER JOIN org on grants.orgID=org.orgid
order by yearStart DESC
我有这个 ^ join,它 运行 本身就很棒。打开行结果并开始遍历它后,我 运行 第二个查询从另一个 table:
执行计数和日期信息
SELECT COUNT(Distinct Event_ID) as ME, MAX(Sample_Date) as MaxD
FROM results where orgid=%d
我需要第一次拉取的数据来获取 ordID,这就是为什么我一次要检查它们的原因
so it runs like this
Query 1
while($row = mysql_fetch_assoc($result)){
Query 2
while($row1 = mysql_fetch_assoc($result1)){
get some data from 2
} //close 2
get some data from 1 and merge with 2
} //close 1
如果不将辅助查询推入其中,它 运行 会非常快速地处理组织中的大约 230 条记录。它减慢了将近 20 秒!我没有正确构建 Count Distinct 吗?结果 table 大约有 100,000 条记录,但我用其他查询浏览了那个东西,它不会像这样陷入困境!如果有帮助,我该如何子查询?
感谢您的任何见解。
假设 results.orgid 已编入索引以排除该问题...
如果使用 JOIN,事情通常会好很多,这样 MySQL 可以优化。子查询的性能可能很差。
如果我正确理解你的关系,试试这个:
SELECT grants.*, org.*, COUNT(Distinct Event_ID) as ME, MAX(Sample_Date) as MaxD
FROM grants
INNER JOIN people on grants.volID=people.vol_id
INNER JOIN org on grants.orgID=org.orgid
LEFT JOIN results ON results.orgid=org.orgid
GROUP BY grants.grantid #whatever your grants PK is
ORDER BY yearStart DESC
不要忘记将 grants.grantid
替换为您的实际资助 PK 列。
要找出查询中的性能瓶颈,您应该做的第一件事是使用数据库的 EXPLAIN 功能,以便它可以告诉您它在做什么。 https://dev.mysql.com/doc/refman/5.0/en/explain.html
听起来您可能没有正确设置某些索引,导致每次循环遍历第一个连接查询的结果时都扫描不必要的行。一种检查方法如下例所示:
首先我有一个测试table
mysql> desc test_table;
+-------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(64) | YES | | NULL | |
| description | text | YES | | NULL | |
| published | datetime | YES | | NULL | |
| updated | datetime | YES | | NULL | |
| status | tinyint(1) | YES | | NULL | |
+-------------+-------------+------+-----+---------+----------------+
6 rows in set (0.02 sec)
mysql> show indexes from test_table;
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test_table | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | |
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.01 sec)
mysql> select count(1) from test_table;
+----------+
| count(1) |
+----------+
| 0 |
+----------+
1 row in set (0.02 sec)
接下来我添加几行
mysql> INSERT INTO test_table (name, description, published, status) VALUES ('name1','description 1 goes here',now(),1),('name2','description 2 goes here',now(),1),('name3', 'description 3 goes here', now(),1);
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select name, description from test_table where status = 1;
+-------+-------------------------+
| name | description |
+-------+-------------------------+
| name1 | description 1 goes here |
| name2 | description 2 goes here |
| name3 | description 3 goes here |
+-------+-------------------------+
3 rows in set (0.01 sec)
接下来我使用数据库中的 EXPLAIN 功能来分析我的查询
mysql> EXPLAIN SELECT name, description, status FROM test_table WHERE name = 'name1' AND status = 1;
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | test_table | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
您可以看到它正在扫描3 行 来查找记录。我怀疑您的数据库正在为第二个查询扫描所有 100K 行,遍历每一行。这意味着如果第一个查询中有 100 个结果,则您有 1000 万行扫描 (100 * 100K)。您希望行列尽可能接近 1,这意味着它将使用索引来查找更快的行。
我现在创建一个索引并包含我希望在我的 WHERE 子句中的列(按照我将添加它们的顺序,注意并非每次都需要使用)
mysql> CREATE INDEX idx_so_example ON test_table (name, description (255), status);
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
接下来我再次尝试 EXPLAIN 并查看数据库如何使用索引并且只扫描了 1 行 。您应该优化您的索引以获得类似的结果。
mysql> EXPLAIN SELECT name, description, status FROM test_table WHERE name = 'name1' AND status = 1;
+----+-------------+------------+------+----------------+----------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+----------------+----------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | test_table | ref | idx_so_example | idx_so_example | 195 | const | 1 | Using index condition |
+----+-------------+------------+------+----------------+----------------+---------+-------+------+-----------------------+
1 row in set (0.01 sec)
对于你的数据库,我会在你的第二个查询中的那 3 列上添加一个复合索引,假设 'results' 是一个基于你的问题的实际 table 名称。
CREATE INDEX idx_some_name ON results (Event_ID, Sample_Date, orgid);
还有一个建议:字段的命名约定应该保持一致,否则会使数据库成为记忆和编码的噩梦。选择一个标准并坚持使用它,所以如果使用 EventId、SampleDate、OrgId 很棒或 event_id、sample_date、org_id,但标准化所有列名称和约定,以便以后尝试的代码中的语法错误更少查询数据。
SELECT *
FROM grants
INNER JOIN people on grants.volID=people.vol_id
INNER JOIN org on grants.orgID=org.orgid
order by yearStart DESC
我有这个 ^ join,它 运行 本身就很棒。打开行结果并开始遍历它后,我 运行 第二个查询从另一个 table:
执行计数和日期信息SELECT COUNT(Distinct Event_ID) as ME, MAX(Sample_Date) as MaxD
FROM results where orgid=%d
我需要第一次拉取的数据来获取 ordID,这就是为什么我一次要检查它们的原因
so it runs like this
Query 1
while($row = mysql_fetch_assoc($result)){
Query 2
while($row1 = mysql_fetch_assoc($result1)){
get some data from 2
} //close 2
get some data from 1 and merge with 2
} //close 1
如果不将辅助查询推入其中,它 运行 会非常快速地处理组织中的大约 230 条记录。它减慢了将近 20 秒!我没有正确构建 Count Distinct 吗?结果 table 大约有 100,000 条记录,但我用其他查询浏览了那个东西,它不会像这样陷入困境!如果有帮助,我该如何子查询?
感谢您的任何见解。
假设 results.orgid 已编入索引以排除该问题...
如果使用 JOIN,事情通常会好很多,这样 MySQL 可以优化。子查询的性能可能很差。
如果我正确理解你的关系,试试这个:
SELECT grants.*, org.*, COUNT(Distinct Event_ID) as ME, MAX(Sample_Date) as MaxD
FROM grants
INNER JOIN people on grants.volID=people.vol_id
INNER JOIN org on grants.orgID=org.orgid
LEFT JOIN results ON results.orgid=org.orgid
GROUP BY grants.grantid #whatever your grants PK is
ORDER BY yearStart DESC
不要忘记将 grants.grantid
替换为您的实际资助 PK 列。
要找出查询中的性能瓶颈,您应该做的第一件事是使用数据库的 EXPLAIN 功能,以便它可以告诉您它在做什么。 https://dev.mysql.com/doc/refman/5.0/en/explain.html
听起来您可能没有正确设置某些索引,导致每次循环遍历第一个连接查询的结果时都扫描不必要的行。一种检查方法如下例所示:
首先我有一个测试table
mysql> desc test_table;
+-------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(64) | YES | | NULL | |
| description | text | YES | | NULL | |
| published | datetime | YES | | NULL | |
| updated | datetime | YES | | NULL | |
| status | tinyint(1) | YES | | NULL | |
+-------------+-------------+------+-----+---------+----------------+
6 rows in set (0.02 sec)
mysql> show indexes from test_table;
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test_table | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | |
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.01 sec)
mysql> select count(1) from test_table;
+----------+
| count(1) |
+----------+
| 0 |
+----------+
1 row in set (0.02 sec)
接下来我添加几行
mysql> INSERT INTO test_table (name, description, published, status) VALUES ('name1','description 1 goes here',now(),1),('name2','description 2 goes here',now(),1),('name3', 'description 3 goes here', now(),1);
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select name, description from test_table where status = 1;
+-------+-------------------------+
| name | description |
+-------+-------------------------+
| name1 | description 1 goes here |
| name2 | description 2 goes here |
| name3 | description 3 goes here |
+-------+-------------------------+
3 rows in set (0.01 sec)
接下来我使用数据库中的 EXPLAIN 功能来分析我的查询
mysql> EXPLAIN SELECT name, description, status FROM test_table WHERE name = 'name1' AND status = 1;
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | test_table | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
您可以看到它正在扫描3 行 来查找记录。我怀疑您的数据库正在为第二个查询扫描所有 100K 行,遍历每一行。这意味着如果第一个查询中有 100 个结果,则您有 1000 万行扫描 (100 * 100K)。您希望行列尽可能接近 1,这意味着它将使用索引来查找更快的行。
我现在创建一个索引并包含我希望在我的 WHERE 子句中的列(按照我将添加它们的顺序,注意并非每次都需要使用)
mysql> CREATE INDEX idx_so_example ON test_table (name, description (255), status);
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
接下来我再次尝试 EXPLAIN 并查看数据库如何使用索引并且只扫描了 1 行 。您应该优化您的索引以获得类似的结果。
mysql> EXPLAIN SELECT name, description, status FROM test_table WHERE name = 'name1' AND status = 1;
+----+-------------+------------+------+----------------+----------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+----------------+----------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | test_table | ref | idx_so_example | idx_so_example | 195 | const | 1 | Using index condition |
+----+-------------+------------+------+----------------+----------------+---------+-------+------+-----------------------+
1 row in set (0.01 sec)
对于你的数据库,我会在你的第二个查询中的那 3 列上添加一个复合索引,假设 'results' 是一个基于你的问题的实际 table 名称。
CREATE INDEX idx_some_name ON results (Event_ID, Sample_Date, orgid);
还有一个建议:字段的命名约定应该保持一致,否则会使数据库成为记忆和编码的噩梦。选择一个标准并坚持使用它,所以如果使用 EventId、SampleDate、OrgId 很棒或 event_id、sample_date、org_id,但标准化所有列名称和约定,以便以后尝试的代码中的语法错误更少查询数据。