从长远来看,哪个PDO SQL查询速度更快,数据量更大?

时间:2017-12-23 19:23:04

标签: mysql pdo query-optimization

从表中有百万记录,当我从中提取数据时,

我想检查所请求的数据是否存在,那么哪条路径效率哪个更快然后另一条?

$Query = '
    SELECT n.id 
    FROM names n 
    INNER JOIN ages a ON n.id = a.aid 
    INNER JOIN regions r ON n.id = r.rid 
    WHERE id = :id
';
$stmt->prepare($Query);
$stmt->execute(['id' => $id]);
if ($stmt->rowCount() == 1) {
    $row = $stmt->fetch();
    ......................
} else {
    exit();
}

$EXISTS = 'SELECT EXISTS (
    SELECT n.fname, n.lname, a.age, r.region 
    FROM names n 
    INNER JOIN ages a ON n.id = a.aid 
    INNER JOIN regions r ON n.id = r.rid 
    WHERE id = :id
    LIMIT 1
)
';
$stmt->prepare($EXISTS);
$stmt->execute(['id' => $id]);
if ($stmt->fetchColumn() == 1) {
    $stmt->prepare($Query);
    $stmt->execute(['id' => $id]);
    $row = $stmt->fetch();
    ......................
} else {
    exit();
}

请注意,idPRIMARY (INT)aid, ridINDEXED (INT)

2 个答案:

答案 0 :(得分:1)

您展示的两种方法几乎肯定是等效的,几乎没有可测量的性能差异。

SELECT n.id 
FROM names n 
INNER JOIN ages a ON n.id = a.aid 
INNER JOIN regions r ON n.id = r.rid 
WHERE id = :id

我认为names.id是该表的主键。主键查找非常快。

然后它将对其他两个表进行二次密钥查找,它将是一个仅索引访问,因为它没有引用这些表的其他列。

您应该学习如何使用EXPLAIN来分析MySQL的优化计划。这是您在想要提高SQL查询性能时应该练习的技能。

请参阅https://dev.mysql.com/doc/refman/5.7/en/using-explain.html

mysql> explain SELECT n.id 
    ->     FROM names n 
    ->     INNER JOIN ages a ON n.id = a.aid 
    ->     INNER JOIN regions r ON n.id = r.rid 
    ->     WHERE id = 1;

+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | n     | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | Using index |
|  1 | SIMPLE      | a     | NULL       | ref   | aid           | aid     | 5       | const |    1 |   100.00 | Using index |
|  1 | SIMPLE      | r     | NULL       | ref   | rid           | rid     | 5       | const |    1 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+

我们看到每个表访问都使用索引(虽然您没有在问题中提供SHOW CREATE TABLE,但我假设索引)。

与使用SELECT EXISTS(...)

的第二个解决方案进行比较
mysql> explain SELECT EXISTS (
    ->     SELECT n.id 
    ->     FROM names n 
    ->     INNER JOIN ages a ON n.id = a.aid 
    ->     INNER JOIN regions r ON n.id = r.rid 
    ->     WHERE id = 1 
    ->     LIMIT 1);

+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+----------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra          |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+----------------+
|  1 | PRIMARY     | NULL  | NULL       | NULL  | NULL          | NULL    | NULL    | NULL  | NULL |     NULL | No tables used |
|  2 | SUBQUERY    | n     | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | Using index    |
|  2 | SUBQUERY    | a     | NULL       | ref   | aid           | aid     | 5       | const |    1 |   100.00 | Using index    |
|  2 | SUBQUERY    | r     | NULL       | ref   | rid           | rid     | 5       | const |    1 |   100.00 | Using index    |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+----------------+

子查询看起来与第一个查询优化计划相同;它仍然以相同的方式使用索引。但它被降级为子查询。可能没什么大不同,但还有一件事。

唯一的好处是SELECT EXISTS...查询保证只返回一行具有true / false值。第一个查询可能返回包含零行,一行或多行的结果集,具体取决于查询中JOIN的匹配程度。差异不是性能差异(除非它返回那么多行,将结果集转移到客户端需要时间,或者使用大量内存来保存客户端中的结果集),但只是为了方便你编码的方式。

答案 1 :(得分:0)

不规范年龄;这只是浪费空间和时间。 age(假设它是'年')可以适合1字节TINYINT UNSIGNED(范围:0..255)并避免JOIN查找。请注意,aid似乎是一个4字节的INT,可以容纳数十亿个不同的值 - 你有数十亿不同的年龄吗?

也许改变regions也是值得的。

在第一个查询中,两个JOINs只会验证年龄和地区中是否有行。那可能浪费。

找到一行时

EXISTS停止。所以LIMIT 1非常不必要。