优化MySQL脚本 - 不同表中的用户,用户地址和国家/地区

时间:2016-09-13 08:37:40

标签: php mysql optimization

我目前正在尝试优化运行 4.9452秒的繁重脚本(查询)。然后有一个PHP while循环,根据返回的内容设置一些数组,但这是另一个问题。

编辑 - 用户表约为9000行,user_address表约为3000行,shop_countries约为200行。

这是我的问题:

SELECT 
            users.id AS user_id,
            users.unique_code AS user_unique_code,
            users.first_name AS user_first_name,
            users.surname AS user_surname,
            users.organisation AS user_organisation,
            users.telephone AS user_telephone,
            users.email AS user_email,
            users.password AS user_password,
            users.newsletter AS user_newsletter,

            user_address.id AS user_address_id,
            user_address.user_id AS user_address_user_id,
            user_address.nickname AS user_address_nickname,
            user_address.address_type AS user_address_type,
            user_address.address_line_1 AS user_address_line_1,
            user_address.address_line_2 AS user_address_line_2,
            user_address.address_line_3 AS user_address_line_3,
            user_address.city AS user_address_city,
            user_address.postcode AS user_address_postcode,
            user_address.country_id AS user_address_country_id,

            shop_countries.printable_name 
            FROM users 
            LEFT JOIN user_address ON users.id = user_address.user_id
            LEFT JOIN shop_countries ON shop_countries.id = user_address.country_id
            WHERE (users.surname != "" OR users.first_name != "")
            ORDER BY users.surname ASC, users.first_name ASC;

我需要选择所有用户,无论他们是否分配了地址,然后在 IS 可用地址时附加所有国家/地区。

以下是EXPLAIN EXTENDED的结果

http://imgur.com/a/UlVX3

到目前为止,似乎如果我要将LEFT JOIN更改为JOIN,那么查询将在不到一秒的时间内执行。问题在于我需要所有用户,无论他们是否有地址。

PHP脚本

对于想要查看相应PHP函数以供我参考设计的人,请参阅以下内容:

function getAllUsersWithAddresses() {

$customers = array();

$sql = 'SELECT 
            users.id AS user_id,
            users.unique_code AS user_unique_code,
            users.first_name AS user_first_name,
            users.surname AS user_surname,
            users.organisation AS user_organisation,
            users.telephone AS user_telephone,
            users.email AS user_email,
            users.password AS user_password,
            users.newsletter AS user_newsletter,

            user_address.id AS user_address_id,
            user_address.user_id AS user_address_user_id,
            user_address.nickname AS user_address_nickname,
            user_address.address_type AS user_address_type,
            user_address.address_line_1 AS user_address_line_1,
            user_address.address_line_2 AS user_address_line_2,
            user_address.address_line_3 AS user_address_line_3,
            user_address.city AS user_address_city,
            user_address.postcode AS user_address_postcode,
            user_address.country_id AS user_address_country_id,

            shop_countries.printable_name 
            FROM users 
            LEFT JOIN user_address ON users.id = user_address.user_id LEFT JOIN shop_countries ON shop_countries.id = user_address.country_id WHERE (users.surname != "" OR users.first_name != "") ORDER BY users.surname ASC, users.first_name ASC;';

$users_query = mysql_query($sql);

$customers = array();

while($row = getData($users_query)) {
    if(!isset($customers[$row['user_id']])) {
        $customers[$row['user_id']] = array(
            'id'           => $row['user_id'],
            'unique_code'  => $row['user_unique_code'],
            'first_name'   => $row['user_first_name'],
            'surname'      => $row['user_surname'],
            'organisation' => $row['user_organisation'],
            'telephone'    => $row['user_telephone'],
            'email'        => $row['user_email'],
            'password'     => $row['user_password'],
            'newsletter'   => $row['user_newsletter']
        );
    }

    if(isset($customers[$row['user_id']]) && !empty($row['user_address_type'])) {
        $customers[$row['user_id']]['addresses'][$row['user_address_type']][] = array(
            'id' => $row['user_address_id'],
            'user_id' => $row['user_address_user_id'],
            'nickname' => $row['user_address_nickname'],
            'address_type' => $row['user_address_type'],
            'address_line_1' => $row['user_address_line_1'],
            'address_line_2' => $row['user_address_line_2'],
            'address_line_3' => $row['user_address_line_3'],
            'city' => $row['user_address_city'],
            'postcode' => $row['user_address_postcode']
        );
    } else {
        $customers[$row['user_id']]['addresses'] = array();
    }
}

return $customers;
}

2 个答案:

答案 0 :(得分:0)

为您的外国ids创建索引

  • user_address.user_id
  • user_address.country_id

答案 1 :(得分:0)

我无法看到你的解释计划 - 如果你在你的问题中发布它会有所帮助。但是,即使我可以,在不了解数据库结构的情况下也没有多大帮助 - 请包含create table语法和索引。在理想世界中,您还应包括索引基数。

只有两种方法可以让查询更快。减少数据库需要检查的行数,或使DBMS更容易找到要返回的行。

我猜大多数费用来自查询中的2个LEFT JOIN。你肯定有完整的国家名单吗?没有那么多。因此,您应该能够使用内部联接替换第二个外部联接。

  

WHERE(users.surname!=“”OR users.first_name!=“”)

在mysql查询中使用“OR”可以从根本上改变其行为。你有很多缺少名字的顾客吗?如果它低于5%,那么......

WHERE LENGTH(users.surname) AND LENGTH(users.first_name)

...超过5%且已编入索引......

WHERE users.surname LIKE '_%' AND users.first_name LIKE '_%'

但最大的胜利来自确保您拥有主键和外键的索引。