• Tidak ada hasil yang ditemukan

Using Your New Database Schema

if ($row[9] == "S") $sign = -1; else $sign = 1;

$dec_lat = $sign*($row[6]+$row[7]/60+$row[8]/3600);

if ($row[14] == "W") $sign = -1; else $sign = 1;

$dec_long = $sign*($row[11]+$row[12]/60+$row[13]/3600);

$query = "INSERT INTO fcc_location (unique_si_loc, lat_deg, lat_min, lat_sec, lat_dir, latitude, long_deg, long_min, long_sec, long_dir, longitude) VALUES ({$row[4]},'{$row[6]}', '{$row[7]}', '{$row[8]}', '{$row[9]}', '$dec_lat','{$row[11]}', '{$row[12]}', '{$row[13]}', '{$row[14]}', '$dec_long')";

$result = @mysql_query($query);

if (!$result) {

// Newer information later in the file: UPDATE instead

$query = "UPDATE fcc_location SET lat_deg='{$row[6]}',

lat_min='{$row[7]}', lat_deg='{$row[8]}', lat_dir='{$row[9]}', latitude='$dec_lat', long_deg='{$row[11]}', long_min='{$row[12]}', long_sec='{$row[13]}', long_dir='{$row[14]}', longitude='$dec_long'

WHERE unique_si_loc='{$row[4]}'";

$result = @mysql_query($query);

if (!$result)

echo "Failure to import location for struc. #{$row[4]} <br>\n";

else

echo "Updated location for struc. #{$row[4]} <br>\n";

} } }

fclose($handle);

}

echo "Done Locations. <br>\n";

?>

Reconstruction Using PHP’s Memory Space

Using PHP to put the data back together isn’t really practical in a production environment. It’s an obvious method if your SQL skills are still new; however, it only works if you’re going to be using a very small set of information. We cover it here to show you how it would work in case you find a valid use for it, but we do so with hesitation. This is neither a sane nor scalable method, and the SQL-based solutions presented in a moment are much more robust. The code in List- ing 5-4 locates all of the towers in Hawaii and consumes a huge amount of memory to do so.

Listing 5-4. Using PHP to Determine the List of Structures in Hawaii

<?php

// Connect to the database

require($_SERVER['DOCUMENT_ROOT'] . '/db_credentials.php');

$conn = mysql_connect("localhost", $db_name, $db_pass);

mysql_select_db("googlemapsbook", $conn);

// Create our temporary holding arrays

$hawaiian_towers = array();

$usi_list = array();

// Get a list of the structures in Hawaii

$structures = mysql_query("SELECT * FROM fcc_structure WHERE struc_state='HI'");

for($i=0; $i<mysql_num_rows($structures); $i++) {

$row = mysql_fetch_array($structures, MYSQL_ASSOC);

$hawaiian_towers[$row['unique_si']] = $row;

$usi_list[] = $row['unique_si'];

}

unset($structures);

// Get all of the owners for the above structures

$owners = mysql_query("SELECT * FROM fcc_owner

WHERE unique_si_own IN (".implode(",",$usi_list).")");

for($i=0; $i<mysql_num_rows($owners); $i++) {

$row = mysql_fetch_array($owners, MYSQL_ASSOC);

$hawaiian_towers[$row['unique_si_own']] =

array_merge($hawaiian_towers[$row['unique_si_own']],$row);

}

unset($owners);

// Figure out the location of each of the above structures

$locations = mysql_query("SELECT * FROM fcc_location

WHERE unique_si_loc IN (".implode(",",$usi_list).")");

for($i=0; $i<mysql_num_rows($locations); $i++) {

$row = mysql_fetch_array($locations,MYSQL_ASSOC);

$hawaiian_towers[$row['unique_si_loc']] =

array_merge($hawaiian_towers[$row['unique_si_loc']],$row);

}

unset($locations);

echo memory_get_usage();

?>

You can see that the only thing this script outputs to the screen is the total memory usage in bytes. For our data set, this is approximately 780KB. This illustrates the fact that this method is very memory-intensive, consuming one-eighth of the average allotment simply for data retrieval. As a result, this method is probably one of the worst ways you could go about reassembling your data. However, this code does introduce the use of the SQL INclause. IN simply takes a list of things (in this case integers) and selects all of the rows where one of the values in the list is in the column unique_si. It’s still better to use joins to take advantage of the SQL engine’s internal optimizations, but INcan be quite handy at times. You can use PHP’s implode()function and a temporary array to create the list to pass to INquickly and easily. For more information about the array_merge()function, check out http://ca.php.net/manual/en/

function.array-merge.php.

The Multitable SELECT Query

Next, you’ll formulate a single query to the database that allows you to retrieve all the data for a single structure as a single row. This means that you could iterate over the entire database doing something with each record as you go, without having a single point in time where you’re consuming a lot of memory for temporary storage. Working from the example we had at the end of Chapter 2, we’re going to replace the static data file with one that is generated with PHP and uses our SQL database of the FCC structures. Due to the volume of data we’ll be limiting the points plotted to only those that are owned and operated in Hawaii. For more data man- agement techniques see Chapter 7. Listing 5-5 shows the new map_data.phpfile. You will either need to zoom in on Hawaii or change your centering in the map_functions.jsfile, too. In Chapter 6, you will work on the user interface for the map, so right now, you will just plot all of the points.

Note

In reality, this approach is primarily shifting the location where you consume the vast amounts of memory. We're pushing the problem off the web server and onto the database server. However, in general, the database server is more capable of handling the load and is optimized explicitly for this purpose.

Listing 5-5. map_data.php: Using a Single SQL Query to Determine the List of Structures

<?php

// Connect to the database

require($_SERVER['DOCUMENT_ROOT'] . '/db_credentials.php');

$conn = mysql_connect("localhost", $db_name, $db_pass);

mysql_select_db("googlemapsbook", $conn);

C H A P T E R 5■ M A N I P U L AT I N G T H I R D - PA RT Y D ATA 108

$query = "SELECT * FROM fcc_structure, fcc_owner, fcc_location WHERE struc_state='HI' AND owner_state='HI'

AND unique_si=unique_si_own AND unique_si=unique_si_loc";

$result = mysql_query($query, $conn);

$joiner = '';

$count = 0;

?>

var markers = [

<?php while($row = mysql_fetch_assoc($result)): ?>

<?= $joiner ?>

{

'latitude': <?= $row['latitude'] ?>, 'longitude': <?= $row['longitude'] ?>,

'name': '<?= addslashes($row['struc_address']) ?>' }

<?

$joiner = ',';

$count++;

?>

<?php endwhile; ?>

];

/* Memory used at the end of the script: <? echo memory_get_usage(); ?> */

/* Output <?= $count ?> points */

You can see that this approach uses a much more compact and easily maintained query, as well as much less memory. In fact, the memory consumption reported by memory_get_usage() this time is merely the memory used by the last fetch operation, instead of all of the fetch operations combined.

The tricky part is the order of the WHEREclauses themselves. The basic idea is to list the WHEREclauses in such an order that the largest amounts of information are eliminated from consideration first. Therefore, having the struc_state='HI'be the first clause removes more than 99.8% of all the data in the fcc_structuretable from consideration. The remaining clauses simply tack on the information from the other two tables that correlates with the 0.2% of remaining information.

Using this map_data.phpscript in the general map template from Chapter 2 gives you a map like the one shown in Figure 5-2. Chapter 6 will expand on this example and help you design and build a good user interface for your map.

Figure 5-2. The FCC structures in Hawaii

Note

Most database engines are smart enough to reorder the WHEREclauses to minimize their workload if they can, and in this case, MySQL would probably do a pretty good job. However, in general, it’s good prac- tice to help the database optimization engine and use a human brain to think about a sane order for the WHEREclauses whenever possible.

A SQL View

The other approach you could take is to create a SQL view on the data and use PHP to select directly from that. A view is a temporary table that is primarily (in our case, exclusively) used for retrieving data from a SQL database. A view is basically the cached result of a query like the one in Listing 5-5, without the state-specific data limitation. You can select from a view in the same way that you can select from an ordinary table, but the actual data is stored across many different tables. Updating is done on the underlying tables instead of the view itself.

Note

Using a SQL view in this way is possible only with MySQL 5.0.1 and later, PostgreSQL 7.1.x and later, and some commercial SQL databases. If you’re using MySQL 3.x or 4.x and would like to use the new view feature, consider upgrading.

Listing 5-6 shows the MySQL 5.x statements needed to create the view.

C H A P T E R 5■ M A N I P U L AT I N G T H I R D - PA RT Y D ATA 110

Listing 5-6. MySQL Statement to Create a View on the Three Tables CREATE VIEW fcc_towers

AS SELECT * FROM fcc_structure, fcc_owner, fcc_location WHERE unique_si=unique_si_own AND unique_si=unique_si_loc

ORDER BY struc_state, struc_type

After the view is created, you can replace the query in Listing 5-5 with the insanely simple

$query = "SELECT * FROM fcc_towers WHERE struc_state='HI' AND owner_state='HI'";and you’re finished.

So why is a view better than the multitable SELECT? Basically, it precomputes all of the cor- relations between the various tables and stores the answer for later use by multiple future queries. Therefore, when you need to select some chunk of information for use in your script, the correlation work has already been done, and the query executes much faster. However, please realize that creating a view for a single-run script doesn’t make much sense, since the value is realized in a time/computation savings over time.

For the next two chapters, we’ll assume that you were successful in creating the fcc_towers view. If your web host doesn’t have a view-compatible SQL installation for you to use, then simply replace our queries in the next two chapters with the larger one from Listing 5-5 and make any necessary adjustments, or find a different way to create a single combined table from all of the data.

Tip

For more information on the creation of views in MySQL, visit http://dev.mysql.com/doc/refman/

5.0/en/create-view.html. To see the limitations on using views, visit http://dev.mysql.com/doc/

refman/5.0/en/view-restrictions.html. For more information on views in PostgreSQL, visit http://

www.postgresql.org/docs/8.1/static/sql-createview.html.