Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP Alternative to using a query within a loop

Tags:

php

mysql

I was told that it is a bad practice to use a query (select) within a loop because it slows down server performance.

I have an array such as

Array ( [1] => Los Angeles )
Array ( [2] =>New York)
Array ( [3] => Chicago )

These are just 3 indexes. The array I'm using does not have a constant size, so sometimes it can contain as many as 20 indexes.

Right now, what I'm doing is (this is not all of the code, but the basic idea)

  1. For loop
  2. query the server and select all people's names who live in "Los Angeles"
  3. Print the names out

Output will look like this:

Los Angeles
      Michael Stern
      David Bloomer
      William Rod

New York
      Kary Mills

Chicago
      Henry Davidson
      Ellie Spears

I know that's a really inefficient method because it could be a lot of queries as the table gets larger later on.

So my question is, is there a better, more efficient way to SELECT information based on the stuff inside an array that can be whatever size?

like image 999
r1nzler Avatar asked Nov 30 '12 10:11

r1nzler


3 Answers

Use an IN query, which will grab all of the results in a single query:

SELECT * FROM people WHERE town IN('LA', 'London', 'Paris')
like image 178
MrCode Avatar answered Sep 19 '22 15:09

MrCode


To further add to MrCodes answer, if you start with an array:-

$Cities = array(1=>'Los Angeles', 2=>'New York', 3=>'Chicago');
$query = "SELECT town, personname FROM people WHERE town IN('".implode("','", $Cities)."') ORDER BY town";
if ($sql = $mysqliconnection->prepare($query)) 
{
    $sql->execute();
    $result = $sql->get_result();
    $PrevCity = '';
    while ($row = $result->fetch_assoc()) 
    {
        if ($row['town'] != $PrevCity)
        {
            echo $row['town']."<br />";
            $PrevCity = $row['town'];
        }
        echo $row['personname']."<br />";
    }
}

As a database design issue, you probably should have the town names in a separate table and the table for the person contains the id of the town rather than the actual town name (makes validation easier, faster and with the validation less likely to miss records because someone has mistyped their home town)

like image 20
Kickstart Avatar answered Sep 18 '22 15:09

Kickstart


As @MrCode mentions, you can use MySQL's IN() operator to fetch records for all of the desired cities in one go, but if you then sort your results primarily by city you can loop over the resultset keeping track of the last city seen and outputting the new city when it is first encountered.

Using PDO, together with MySQL's FIELD() function to ensure that the resultset is in the same order as your original array (if you don't care about that, you could simply do ORDER BY city, which would be a lot more efficient, especially with a suitable index on the city column):

$arr = ['Los Angeles', 'New York', 'Chicago'];
$placeholders = rtrim(str_repeat('?, ', count($arr)), ', ');

$dbh = new PDO("mysql:dbname=$dbname", $username, $password);
$qry = $dbh->prepare("
  SELECT   city, name
  FROM     my_table
  WHERE    city IN ($placeholders)
  ORDER BY FIELD(city, $placeholders)
");

if ($qry->execute(array_merge($arr, $arr))) {

  // output headers
  echo '<ul>';

  $row = $qry->fetch();
  while ($row) {
    $current_city = $row['city'];

    // output $current_city initialisation
    echo '<li>'.htmlentities($current_city).'</li><ul>';
    do {
      // output name $row
      echo '<li>'.htmlentities($row['name']).'</li>';
    } while ($row = $qry->fetch() and $row['city'] == $current_city);
    // output $current_city termination
    echo '</ul>';
  }

  // output footers
  echo '</ul>';
}
like image 21
eggyal Avatar answered Sep 20 '22 15:09

eggyal