高效的Foreach循环查询数据库

I am displaying images on my website with their details, however of late the site is very slow at loading.

The main thing is this foreach loop that loops through 100 times to display 100 posts in a grid. It takes 14 seconds to run

foreach($posts as $post) {

            $hashtags[] = $this->HashTagsModel->get_hashtags($post["id"]);
            $author[] = $this->UserModel->get_user_details($post["user_id"]);
            $comment_count[] = $this->CommentModel->get_comments_count($post["id"]);
            $is_favourited[] = $this->FavouriteModel->is_favourited($post["id"]);
            $is_reposted[] = $this->RepostModel->is_reposted($post["id"]);
            $vote_status[] = $this->vote_status($post["id"]);
            $comments[] = $this->count_comments($post["id"]);

        }

How can I do this differently to make it more efficient? This worked before our websites database became massive

Any help would be appreciated,

regards,

Almost Fired

The efficient way to foreach loop query database is to not foreach query database. This is because you are allowing an unknown amount of queries to be fired off which will cause massive queueing. What if suddenly 5000 images get added, do you loop through them all? That query will take a very long time.

You have $post["id"] as your where variable I am assuming, so you could reduce this process significantly by doing a single query after formulating an array of post ids, something like this:

$postids = array();
foreach($posts as $post) {
    $postids[] = $post['id'];
}

// Selecting from 1 table
$query = 'SELECT * FROM hashtags WHERE id IN ('. implode(",", $postids) .')';

This would fetch all the information on hashtags where the id is is one of your postids. That is just 1 table, you would likely want to fetch multiple, without knowing your database structure I'm going to be generic, so something like:

// Selecting and joining data from multiple tables
$query = ' SELECT author.name, table.col FROM posts
LEFT JOIN author ON author.id = post.author_id
LEFT JOIN table ON table.id = post.table_id
WHERE posts.id IN IN ('. implode(",", $postids) .')';

It is a bit more difficult to be more accuracy. I think joining tables would provide you a better result, you can even join counts for votes/comments. If that is not possible, you could query all data related to your posts and then formulate it in PHP, then you know exactly how many queries you have. For example, change your models to accept an array instead of a single post ID and then change your "WHERE post_id = x" to "WHERE post_id IN (x)" then you can do something like:

$postids = array();
foreach($posts as $post) {
    $postids[] = $post['id'];
}

$hashtags = $this->HashTagsModel->get_hashtags($postids);
$author = $this->UserModel->get_user_details($postids);
$comment_count = $this->CommentModel->get_comments_count($postids);
$is_favourited = $this->FavouriteModel->is_favourited($postids);
$is_reposted = $this->RepostModel->is_reposted($postids);
$vote_status = $this->vote_status($postids);
$comments = $this->count_comments($postids);

This gets your queries outside of the loop, and you know there will only ever be 7 SQL queries, and not queries * posts. In PHP you would loop through the results of each array to assign each one back to posts based on its ID.