Justin Erswell
Justin Erswell

Reputation: 708

MySQL Query Performance and Codeigniter

I have this MySQL query which I am loading in to my home controller and after running Codeigniter's $this->output->enable_profiler(TRUE); I get an execution time of 5.3044

The Query inside my model:

class Post extends CI_Model {

    function stream($uid, $updated, $limit) {
        $now = microtime(true);
        $sql = "
            SELECT 
                * 
            FROM 
                vPAS_Posts_Users_Temp 
            WHERE 
                post_user_id = ? 
                AND post_type !=4 
                AND post_updated > ?
                AND post_updated < ? 
            UNION 

            SELECT 
                u.* 
            FROM 
                vPAS_Posts_Users_Temp u 
            JOIN 
                PAS_Follow f 
                ON f.folw_followed_user_id = u.post_dynamic_pid 
                WHERE u.post_updated > ?
                AND post_updated < ? 
                AND (( f.folw_follower_user_id = ? AND f.folw_deleted = 0 ) 
                OR ( u.post_passed_on_by = f.folw_follower_user_id OR u.post_passed_on_by = ? AND u.post_user_id != ? AND u.post_type =4 )) 
            ORDER BY 
                post_posted_date DESC 
            LIMIT ?
        "; 

        $query = $this->db->query($sql, array($uid, $updated, $now, $updated, $now, $uid, $uid, $uid, $limit));

        return $query->result();
    }

}

Is there anything I can do here to improve the execution time and therefore increase my page load?

Edit

Explain Results MySQL Explain Results

MySQL Workbench Visual Explain

MySQL Workbench Visual Explain

Upvotes: 4

Views: 8041

Answers (5)

Federico J.
Federico J.

Reputation: 15912

Maybe you won't believe it, but DON'T retrieve SELECT * in your SQL. Just write the fields you want to retrieve and I think it'll speed up a lot.

I've seen increases in speed of more than 20 times when executing a query (from 0.4secs to 0.02 secs) just changing * for required fields.

Other thing: If you have an auto_increment id on INSERT in your tables, DON'T use post_posted_date as ORDER field. Ordering by DATETIME fields is slow, and if you may use an INT id (which hopefully you will have as an index) you will achieve the same result quicker.

UPDATE

As required in the question, technical reasons:

Bonus: Learning how to set the indexes: http://ronaldbradford.com/blog/tag/covering-index/

Upvotes: 3

Nouphal.M
Nouphal.M

Reputation: 6344

I think you can remove the UNION from the query and make use of left join instead and avoid the unnecessary conditions:

SELECT U.* 
FROM vPAS_Posts_Users_Temp AS U 
LEFT JOIN PAS_Follow AS F ON F.folw_followed_user_id = U.post_dynamic_pid 
WHERE U.post_updated > ?
AND U.post_updated < ?
AND ( 
      ( 
        F.folw_follower_user_id = ? AND F.folw_deleted = 0 
      ) 
      OR 
      ( 
        U.post_passed_on_by = F.folw_follower_user_id OR U.post_passed_on_by = ? 
      )
    ) 
ORDER BY 
U.post_posted_date DESC 
LIMIT ?

Also identify and set proper indexes in your tables.

Upvotes: 0

Minhaz
Minhaz

Reputation: 446

Try the query with left join as you are trying to union on same table

"SELECT 
u.* 
        FROM 
            vPAS_Posts_Users_Temp u
            LEFT JOIN PAS_Follow f ON f.folw_followed_user_id = u.post_dynamic_pid 
        WHERE (
        u.post_user_id = ? 
                AND u.post_type !=4 
                AND u.post_updated > ?
                AND u.post_updated < ?
    ) 
    OR 
     (
    u.post_updated > ?
        AND post_updated < ? 
        AND (( f.folw_follower_user_id = ? AND f.folw_deleted = 0 ) 
                OR ( u.post_passed_on_by = f.folw_follower_user_id OR u.post_passed_on_by = ? AND u.post_user_id != ? AND u.post_type =4 )) 
    )
        ORDER BY 
            u.post_posted_date DESC 
            LIMIT ?"

Upvotes: 0

Pattle
Pattle

Reputation: 6016

I would add indexes on post_user_id, post_updated and folw_follower_user_id.

In this case it may also be better to not use union and separate the query into two separate ones and then use PHP to combine to two result sets.

If you switched to using active record you could also look into caching, to get better performance

Upvotes: 1

Kao
Kao

Reputation: 2272

The rows column shows an estimate for how many rows needs to be examined, which as I understand, means that in your case, it has to scan 72 * 1 * 2627 * 1 * 2 rows, which is quite a lot.

Now, the trick is to bring down this number, and one way is to add indexes. In your case, I would suggest adding an index which contains: post_user_id, post_type, post_updated, post_updated.
This should bring down the first result set, of 72 rows.

Now for the UNION, try using UNION ALL instead, as it is claimed to be faster.
If that doesn't fix the problem, I would suggest rewriting the query to not use a UNION call at all.

Upvotes: 0

Related Questions