Link to home
Start Free TrialLog in
Avatar of WarriorPoet42
WarriorPoet42

asked on

Dynamically Sorting Cached Data

Okay, I have a PHP page that retrieves data from a mySQL DB.  The page is going to be written in such a way that it serves a cached version of itself unless the caches is greater than five minutes old, in which case it updates.

But currently the page uses links in the table to pass GET parameters to itself to adjust the sort/criteria of the data presented (basically adding optional WHERE and ORDER BY clauses).  How can cache the data and allow users to dynamically sort it - while still realizing at least some of the CPU savings I was gunning for when I staticized this page?
Avatar of akshah123
akshah123
Flag of United States of America image

Well, to be frank, I have not tried anything like this where I was serving cached php pages to the client.  Using cache to improve the efficiency of the pages really depends on each individual situation.  Personally, I rely on the mysql's ability to cache the query.  MySQL will give a cached version of your query as long as it is the same byte to byte.  Even an extra space would cause MySQL to rerun the query.  Since you will have optional where and order by clauses, you will not be able to use mysql's cached functionality.  

In that case, if it is pausible, you may want to get all the records from the database and sort the results in php itself using multiarray sort.  However, if the criteria significantly narrows down the results (from 100,000 records to 100-1000) this solution is not desirable.

You also have other option of using javascript to sort the results for you.  This way client's computer does the work for you but you will need to have a good command of ajax. http://www.brainjar.com/dhtml/tablesort/default.asp

Avatar of WarriorPoet42
WarriorPoet42

ASKER

I have NO command of ajax.  Or JSP. :/
if your sorting options is limited to one or two options, e.g either sort by price or name, then a nasty work around that could be done, create two version of teh cashed file, once ordered by name and the other by price
still, there are some other options that are a bit complicated. you can cash the database itself as an xml file and make your page dynamic via javascript, but needs some craft with javascript.
ajax as akshah123  mentioned is not bad at all, there are some ready made javascript comonents that can do the sorting for you, some are damn cool too
http://www.activewidgets.com/grid/
it is an expensive component, but so powerful, and just an exampl what javascript can do
There are three sort options - one is just a sort, the other two are filters.  One filter has 3 options, the other has 16. 96 total possibilites.  I don't think the crufty method will work. ;p
ASKER CERTIFIED SOLUTION
Avatar of Richard Quadling
Richard Quadling
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
So. You now want to use your cached data. You need to include it. As you already have the logic to determine if the cache is "stale" and you need to refresh the cache handled, the code which follows is going to use the cached data.

An easy way around this issue sometimes is to ONLY use the cached data. Saves having 2 lots of coding doing the same thing with different sources (1 being the live db data, the other being the cached data).

Let's deal with sorting first.

Your page allows your users to sort by Name, DOB and Value. Both in Ascending and Descending order.

So. Try this ...

<?php
$a_people_data = array (
  0 =>
  array (
    'Name' => 'Richard Quadling',
    'DOB' => '1967/10/01',
    'Value' => 1726,
  ),
  1 =>
  array (
    'Name' => 'Sally Williamson',
    'DOB' => '1974/08/24',
    'Value' => 425,
  ),
  2 =>
  array (
    'Name' => 'Hesper Ruth',
    'DOB' => '2003/07/01',
    'Value' => 21,
  ),
  3 =>
  array (
    'Name' => 'Thomas Roy',
    'DOB' => '2005/03/05',
    'Value' => 4,
  ),
);

$a_people_data_by_column = array();
foreach($a_people_data as $i_row => &$a_person)
      {
      foreach($a_person as $s_column => $m_value)
            {
            $a_people_data_by_column[$s_column][$i_row] = $m_value;
            }
      }
$s_sort_by = 'Name';
$i_sort_order = SORT_ASC;
if (isset($_GET['SortBy']) && in_array($_GET['SortBy'], array_keys($a_people_data_by_column)))
      {
      $s_sort_by = $_GET['SortBy'];
      }
if (isset($_GET['SortDir']) && in_array($_GET['SortDir'], array('Asc', 'Desc')))
      {
      $i_sort_order = ($_GET['SortDir'] === 'Asc') ? SORT_ASC : SORT_DESC;
      }
array_multisort($a_people_data_by_column[$s_sort_by], $i_sort_order, $a_people_data_by_column['Name'], $a_people_data_by_column['DOB'], $a_people_data_by_column['Value']);

echo '<pre>' . var_export($a_people_data_by_column, True) . '</pre>';
?>

The first thing I've done is converted the data from row to columns (the array_multisort() works a LOT easier that way).

Then I see if SortBy and/or SortDir have been supplied. If so, I use them as overrides for the default sort order.

Then I sort the array. Note that I've had to include the all array columns. I'm not sure I really need to, but I've done so. Play with that if you want.

So. Save this file as cached.php and then run it.

Now run it as ....

cached.php?SortBy=Value
cached.php?SortBy=Value&SortDir=Desc
cached.php?SortBy=DOB
cached.php?SortBy=DOB&SortDir=Desc


So far so good. We have now sorted our cached data depending upon the user request. Specific column and asc/desc support.


Oh. Unsorted ...

array (
  'Name' =>
  array (
    0 => 'Hesper Ruth',
    1 => 'Richard Quadling',
    2 => 'Sally Williamson',
    3 => 'Thomas Roy',
  ),
  'DOB' =>
  array (
    0 => '2003/07/01',
    1 => '1967/10/01',
    2 => '1974/08/24',
    3 => '2005/03/05',
  ),
  'Value' =>
  array (
    0 => 21,
    1 => 1726,
    2 => 425,
    3 => 4,
  ),
)

Sorted by value cached.php?SortBy=Value

array (
  'Name' =>
  array (
    0 => 'Thomas Roy',
    1 => 'Hesper Ruth',
    2 => 'Sally Williamson',
    3 => 'Richard Quadling',
  ),
  'DOB' =>
  array (
    0 => '2005/03/05',
    1 => '2003/07/01',
    2 => '1974/08/24',
    3 => '1967/10/01',
  ),
  'Value' =>
  array (
    0 => 4,
    1 => 21,
    2 => 425,
    3 => 1726,
  ),
)


Sorted by Namedesceding ?SortBy=Name&SortDir=Desc

array (
  'Name' =>
  array (
    0 => 'Thomas Roy',
    1 => 'Sally Williamson',
    2 => 'Richard Quadling',
    3 => 'Hesper Ruth',
  ),
  'DOB' =>
  array (
    0 => '2005/03/05',
    1 => '1974/08/24',
    2 => '1967/10/01',
    3 => '2003/07/01',
  ),
  'Value' =>
  array (
    0 => 4,
    1 => 425,
    2 => 1726,
    3 => 21,
  ),
)
So now onto the filtering part.

Hopefully you can see where this plugs in?

If not ... you do it as part of the conversion of row data (the original cached data) into the column data (the format used for the array_multisort()).

What filters could you need though will dictate how to code them.

If you can come back with comments on the above code, then we can proceed.

Ok.

I've just reread through this. A lot to digest. As with your previous question, you need to implement the code/ideas here into your pages. The mechanism is sound. It is safe from abuse (i.e. you can certainly TRY to sort by a column that doesn't exist!).

It /is/ a lot.  I'm writing a piece at a time for review.  I don't note a mysql_fetch_all function.  mysql_fetch_array returns a single row.  Assuming $a_people_data is my multidimention array, how would I append multiple rows to it?

while ($row=mysql_fetch_array($result)) {
   //take $row, and integrate it with $a_people_data
}
$a_people_data = array();
while(False !== ($a_people_data[] = mysql_fetch_assoc($result)));

Should do the trick.

Some of the dbs have a fetch all method. I use my own class which does this for me rather than calling the above code manually. Write Once Use Many.
Note: I use mysql_fetch_assoc() and not mysql_fetch_array().

Why?

I want the column names to be used as part of the data.

If you use mysql_fetch_array() then the columns are just numbered.

This is ok, but the filtering/sorting code would need to be amended to look at numbers instead.

I think it is quite OK to allow a user to see that sorting by name shows SortBy=Name in the URL.

Actually, in my experience, mysql_fetch_array allows both byassoc and bynum and defaults to byassoc.  In fact, all of my code references fields by field names, and I use mysql_fetch_array everywhere.
Okay, I have this code to cache the data, and it is working beautifully.

<?php
 include("sqlauth.inc");
 $connect = mysql_connect($host,$account,$password);
 $db = mysql_select_db($dbname,$connect);
 
 $s_filename = './cache/players_data.txt';

 $sql="SELECT COUNT(server_id) AS `count` FROM servers";
 $result = mysql_query($sql,$connect) or die(mysql_error());
 $row = mysql_fetch_array($result);
 $servercount=$row['count'];

 $sql = "SELECT COUNT(char_badges_1.badge_id) AS count, characters.*, servers.server_name FROM char_badges_1, characters, servers ";
 $sql .= "WHERE (char_badges_1.char_id=characters.char_id AND characters.priv=0 AND characters.char_server=servers.server_id) GROUP BY char_id ";
 for ($i=2;$i<=$servercount;$i++) {
       $sql .= "UNION SELECT COUNT(char_badges_".$i.".badge_id) AS count, characters.*, servers.server_name FROM char_badges_".$i.", characters, servers  ";
       $sql .= "WHERE (char_badges_".$i.".char_id=characters.char_id AND characters.priv=0 AND characters.char_server=servers.server_id) GROUP BY char_id ";
 }
 $result = mysql_query($sql,$connect) or die(mysql_error());
 $fullqry = array();
 while(False !== ($fullqry[] = mysql_fetch_assoc($result)));
 //echo var_export($fullqry, True);
 file_put_contents($s_filename, '<?php $a_people_data = ' . var_export($fullqry, True) . '; ?>');
?>

I will now proceed with the next coding steps.
Excellent. Don't forget that the players_data.txt file is actually valid PHP code!!!!

I personally would call it an .inc file.

From the PHP perspective, a .inc or a .inc.php looks more like an include file for PHP than a txt file which may be plain text.

Simply looks better.

Hey not only are you now writing PHP code, you are writing PHP code which writes PHP code!

Cool eh!

Now just imagine writing PHP code which creates JS code. Now you can do AJAX! Nearly!
I am having problems with step 2.  I tried to convert the code to fit my sorts/filters, but I have clearly done something wrong.

Currently there are three gets in the url based on user input:
faction=Villains // This is a filter, two options and an "all"
server=Defiant // This is a filter, 15 options and an "all"
sort=nameasc // This is a sort.  Choices are nameasc (SORT BY char_name ASC), namedesc (SORT BY char_name DESC), and the default sorting method which is (SORT BY count DESC, char_name ASC).

The following code:

  $fullqry_by_col = array();
  foreach($fullqry_by_col as $i_row => &$onerow) {
   foreach($rowrow as $s_column => $m_value) {
    $fullqry_by_col[$s_column][$i_row] = $m_value;
   }
  }
  $s_sort_by = 'count';
  $i_sort_order = SORT_DESC;
  if (isset($_GET['sort']) && in_array($_GET['sort'], array_keys($fullqry_by_col))) {
   $s_sort_by = $_GET['sort'];  
  }
  if (isset($_GET['sort']) && in_array($_GET['sort'], array('Asc', 'Desc'))) {
   $i_sort_order = ($_GET['sort'] === 'nameasc') ? SORT_ASC : SORT_DESC;
  }
  echo $s_sort_by."<br>.".$i_sort_order;
  array_multisort($fullqry_by_col[$i_sort_by], $i_sort_order, $fullqry_by_col['char_name'], $fullqry_by_col['char_faction'], $fullqry_by_col['char_lvl']);
  echo '<pre>' . var_export($fullqry_by_col, True) . '</pre>';
 }
 Is producing the following error:

Warning: array_multisort() [function.array-multisort]: Argument #1 is expected to be an array or a sort flag in /home/badgmup1/public_html/players_get_data.php on line 35

array (
)
Besides some typos, I think I have a handle on how array_multisort works.  I now get output.  My code is as follows:

<?php
 
// -------------------------------------------
 function getcache ($cachefile) {
  require($cachefile);
  $arr_by_col = array();
  foreach($fullqry as $i_row => &$onerow) {
   foreach($onerow as $s_column => $m_value) {
    $arr_by_col[$s_column][$i_row] = $m_value;
   }
  }
  return $arr_by_col;
 }
// -------------------------------------------

 include("sqlauth.inc");
 $connect = mysql_connect($host,$account,$password);
 $db = mysql_select_db($dbname,$connect);
 $sql="SELECT * FROM users WHERE user_email=".$_SESSION['email'];  
 $result=mysql_query($sql, $connect) or die(mysql_error());
 $row=mysql_fetch_array($result);
 
 if ($row['user_priv']>=1) {
       $i_delay = 10;
 }
 else {
       $i_delay = 300;
 }

 switch ($_GET['sort']) {
       case "nameasc":
       case "namedesc":
        $sortcol1='char_name';
        $sortcol2='count';
        $sortcol1by=SORT_ASC;
        $sortcol2by=SORT_DESC;
        $sortcol1on=SORT_STRING;
        $sortcol2on=SORT_NUMERIC;
        if ($_GET['sort']=='namedesc') $sortcol1by=SORT_DESC;
        break;
       case "":
       default:
        $sortcol1='count';
        $sortcol1by=SORT_DESC;
        $sortcol2='char_name';
        $sortcol2by=SORT_ASC;
        $sortcol1on=SORT_NUMERIC;
        $sortcol2on=SORT_STRING;
        break;
 }

 if (file_exists($s_filename) && (filemtime($s_filename) >= (time() - $i_delay))) {
  $fullqry_by_col = getcache('./cache/players_data.txt');
  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['char_faction'], $fullqry_by_col['char_lvl'], $fullqry_by_col['server_name']);
  echo '<pre>' . var_export($fullqry_by_col, True) . '</pre>';
 }
 else {
  require("players_cache.php");
  $fullqry_by_col = getcache('./cache/players_data.txt');
  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $sortcol1on, $fullqry_by_col[$sortcol2], $sortcol2by, $sortcol2on, $fullqry_by_col['char_faction'], $fullqry_by_col['char_lvl'], $fullqry_by_col['server_name']);
  echo '<pre>' . var_export($fullqry_by_col, True) . '</pre>';  
 }
?>

I get output, and it seems to be more or less correct output.  However, before the output, I get this error:

Warning: Invalid argument supplied for foreach() in /home/badgmup1/public_html/players_get_data.php on line 9.  Line 9 being the second, inner foreach.

Additionally, even using SORT_STRING, the sorts seem to treat A-Z > a-z.

I guess now it is time to figure out filtering.
What does ...

echo $s_sort_by."<br>.".$i_sort_order;

report and what is in $_GET?

(Back tomorrow. I finish at 4pm UK time.)
The reason I use fetch_assoc SPECIFICALLY is that I ONLY want the column names as keys. I don't want the numeric indices.

That's just me.

Can you supply a URL for the txt file?
Can you supply a URL which calls this code too?

Oh. The last message I sent was SUPPOSED to be around 4pm yesterday, but something went wrong our end I just refreshed and it was sent just now.


Raw data: http://www.badge-whore.com/cache/player_data.txt
Code: http://www.badge-whore.com/players_get_data.php

I've just been typing fields directly into the url to suppy various GETs.  After some research, it seems that A-Z > a-z is the way array_multisort always works, and you have to jump through hoops to do otherwise.  I can deal with that later, however.

The next step is filtering the data, converting the data from cols BACK to rows, and looping through the array to fill the data.

The original page is at http://www.badge-whore.com/players.php.  Note that it says it is serving cached data - its lying for now. ;p
You don't need to convert back to rows.

foreach($$fullqry_by_col[0] as $i_row => $m_junk)
 {
 // Now draw the results from the ORIGINAL data using $i_row as the index.
 }
Yes.

PHP Manual.

Both SORT_STRING and SORT_REGULAR are case sensitive, strings starting with a capital letter will come before strings starting with a lowercase letter.

To perform a case insensitive search, force the sorting order to be determined by a lowercase copy of the original array.

Which you COULD do as part of the cloning ...

$fullqry_by_col[$s_column][$i_row] = $m_value;

becomes

$fullqry_by_col[$s_column][$i_row] = is_string($m_value) ? strtolower($m_value) : $m_value;

Now sorting will be case insensitive.

If I do that, though, then won't my data be displayed as a lowercase value?
No. Only the data by col was altered to lowercase. The cached data is untouched. Or at least it should be. My examples have made this assumption.

Ah.  I am confused, then about how the foreach statement is working.  I guess it might be easier if I could find what => does, but searches don't like to help me with that one - ignoring special characters, I fear.

When I do foreach($$fullqry_by_col[0] as $i_row => $m_junk)  isn't $i_row the assoc names of my fields?  How is this giving me the order to display the original data?
OOps. too many $$ there.

An array has a key/index and a value.

foreach($array as $value)

or

foreach($array as $key => $value)

if you want to know the key as well as the value.

From the PHP Manual ...

PHP 4 introduced a foreach construct, much like Perl and some other languages. This simply gives an easy way to iterate over arrays. foreach works only on arrays, and will issue an error when you try to use it on a variable with a different data type or an uninitialized variable. There are two syntaxes; the second is a minor but useful extension of the first:

foreach (array_expression as $value)
   statement
foreach (array_expression as $key => $value)
   statement
The first form loops over the array given by array_expression. On each loop, the value of the current element is assigned to $value and the internal array pointer is advanced by one (so on the next loop, you'll be looking at the next element).

The second form does the same thing, except that the current element's key will be assigned to the variable $key on each loop.

Ah. I just noticed that you have a dead element in the array.

So, just before you save the array to the txt file ...

array_pop($fullqry);

to remove the last entry. Currently this is ...

  1050 => false,

Not good.
Code added, dead element gone.
The $fullqry_by_col data is ONLY for use in the sorting and filtering. The orignal data ($fullqry) is untouched.

You filter and convert to columns and then sort it.

You use the sorted data (only the columns that can be sorted and the unique ID are necessary) and as you are only supporting sorting by name, only the name column is actually required, along with the unique ID column.

Hmmm..

How about this ...

<?php
include_once('./cache/players_data.txt');

$fullqry_by_col = array();
foreach($fullqry as $i_row => &$onerow)
      {
      $fullqry_by_col['ORIGINAL_ROW'][$i_row] = $i_row; // Track ORIGINAL row number during sorting.
      foreach($onerow as $s_column => $m_value)
            {
            $fullqry_by_col[$s_column][$i_row] = is_string($m_value) ? strtolower($m_value) : $m_value; // Use lower case for sorting.
            }
      }

$sortcol1='count';
$sortcol1by=SORT_DESC;
$sortcol2='char_name';
$sortcol2by=SORT_ASC;
$sortcol1on=SORT_NUMERIC;
$sortcol2on=SORT_STRING;
if (isset($_GET['sort']))
      {
      switch ($_GET['sort'])
            {
            case "nameasc" :
            case "namedesc" :
                  $sortcol1 = 'char_name';
                  $sortcol2 = 'count';
                  $sortcol1by = ($_GET['sort'] == 'namedesc') ? SORT_DESC : SORT_ASC;
                  $sortcol2by = SORT_DESC;
                  $sortcol1on = SORT_STRING;
                  $sortcol2on = SORT_NUMERIC;
                  break;
            }
      }

array_multisort
      (
      $fullqry_by_col[$sortcol1],
      $sortcol1by,
      $fullqry_by_col[$sortcol2],
      $sortcol2by,
      $fullqry_by_col['ORIGINAL_ROW']            
      );
foreach($fullqry_by_col['ORIGINAL_ROW'] as $i_sorted_row => $i_original_row) // use the sorted original row "column" to map to original data.
      {
      echo "$i_sorted_row => {$fullqry[$i_original_row]['char_name']} {$fullqry[$i_original_row]['char_lvl']}<br />";
      }
?>

Using this and the data you provided if I try the URL without sorting I get ...

0 => Name:Atomic Wedgie Level:50 Count:331
1 => Name:Badge Masta Level:45 Count:331
2 => Name:God' of Darkness Level:23 Count:330
3 => Name:Beef Cake Level:50 Count:325
4 => Name:FireWater Level:50 Count:325
5 => Name:Markus V8.0 Level:50 Count:324
6 => Name:Hell Minion Level:50 Count:321
7 => Name:Maxwell Blast Level:50 Count:321
8 => Name:Avvatar Level:50 Count:320
9 => Name:Fireflyte Level:50 Count:320
10 => Name:Fusion Blast Level:50 Count:320
...

Use ?sort=nameasc I get ...

0 => Name:.Demonico Level:50 Count:248
1 => Name:0roro Level:50 Count:314
2 => Name:98th Engineer Level:50 Count:277
3 => Name:A 2 Brutaa Level:41 Count:163
4 => Name:A C Cidental Level:9 Count:6
5 => Name:A.I 101 V2 Level:40 Count:103
6 => Name:Abaleno Lutrai Level:40 Count:139
7 => Name:Abby. Level:19 Count:23
8 => Name:Absinthe d'Nuit Level:50 Count:250
9 => Name:absolut law Level:45 Count:222
10 => Name:Adara Hex Level:13 Count:24
...

and using ?sort=namedesc I get ...

0 => Name:Zeophex Level:50 Count:300
1 => Name:Zelus Level:26 Count:59
2 => Name:Zedd Level:50 Count:129
3 => Name:Zechs Level:40 Count:168
4 => Name:Zazie the Beast Level:20 Count:5
5 => Name:Zarl Level:50 Count:319
6 => Name:Zarine Level:4 Count:4
7 => Name:Zahar Level:10 Count:12
8 => Name:Zach Level:42 Count:264
9 => Name:Yukie Bikouchi Level:35 Count:182
10 => Name:Yojinbo-sama Level:50 Count:255
...



Read the few comments I've added.
Oh! The $sortcolxon vars are not needed as you don't want numeric strings sorted as strings but as numbers. This is the only time you need to really worry about this.

1,2,10,11 is the right order.

as is

"1","2","10","11"

If you needed

"1","10","11","2"

then you would need to use the sorted on stuff.
I am getting the following errors:

Warning: array_multisort() [function.array-multisort]: Argument #1 is expected to be an array or a sort flag in /home/badgmup1/public_html/players_get_data.php on line 53

53:  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);

Warning: Invalid argument supplied for foreach() in /home/badgmup1/public_html/players_get_data.php on line 61

61:  foreach($fullqry_by_col['ORIGINAL_ROW'] as $i_sorted_row => $i_original_row) // use the sorted original row "column" to map to original data.

Full source follows.
<?php
 session_start();

// -------------------------------------------
 function getcache ($cachefile) {
  require($cachefile);
  foreach($allqry as $i_row => &$onerow) {
   $allqry_by_col['ORIGINAL_ROW'][$i_row] = $i_row; // Track ORIGINAL row number during sorting.
   foreach($onerow as $s_column => $m_value) {
    $allqry_by_col[$s_column][$i_row] = is_string($m_value) ? strtolower($m_value) : $m_value; // Use lower case for sorting.
   }
  }
 }
// -------------------------------------------

 include("sqlauth.inc");
 $connect = mysql_connect($host,$account,$password);
 $db = mysql_select_db($dbname,$connect);
 $sql="SELECT * FROM users WHERE user_email=".$_SESSION['email'];  //FIX THIS BEFORE LIVE ROLLOUT
 $result=mysql_query($sql, $connect) or die(mysql_error());
 $row=mysql_fetch_array($result);
 
 if ($row['user_priv']>=1) {
  $i_delay = 30;
 }
 else {
  $i_delay = 300;
 }

 $data_file='./cache/players_data.txt';

 $sortcol1='count';
 $sortcol1by=SORT_DESC;
 $sortcol2='char_name';
 $sortcol2by=SORT_ASC;
 if (isset($_GET['sort'])) {
  switch ($_GET['sort']) {
   case "nameasc" :
   case "namedesc" :
    $sortcol1 = 'char_name';
    $sortcol2 = 'count';
    $sortcol1by = ($_GET['sort'] == 'namedesc') ? SORT_DESC : SORT_ASC;
    $sortcol2by = SORT_DESC;
    $sortcol1on = SORT_STRING;
    $sortcol2on = SORT_NUMERIC;
    break;
  }
 }

 if (file_exists($data_file) && (filemtime($data_file) >= (time() - $i_delay))) {
  echo "Cache";
  $fullqry_by_col = getcache('./cache/players_data.txt');
  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);
 }
 else {
  echo "Refresh";
  require("players_cache.php");
  $fullqry_by_col = getcache('./cache/players_data.txt');
  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);
 }
 foreach($fullqry_by_col['ORIGINAL_ROW'] as $i_sorted_row => $i_original_row)  {  // use the sorted original row "column" to map to original data.    
  echo "$i_sorted_row => {$fullqry[$i_original_row]['char_name']} {$fullqry[$i_original_row]['char_lvl']}<br />";
 }
?>
With the code I gave or your code?

My code is actually working right now!!!!

/ -------------------------------------------
 function getcache ($cachefile) {
  require($cachefile);
  foreach($allqry as $i_row => &$onerow) {
   $allqry_by_col['ORIGINAL_ROW'][$i_row] = $i_row; // Track ORIGINAL row number during sorting.
   foreach($onerow as $s_column => $m_value) {
    $allqry_by_col[$s_column][$i_row] = is_string($m_value) ? strtolower($m_value) : $m_value; // Use lower case for sorting.
   }
  }
 }
// -------------------------------------------


becomes


/ -------------------------------------------
 function getcache ($cachefile) {
  require($cachefile);
  $allqry_by_col = array();
  foreach($allqry as $i_row => &$onerow) {
   $allqry_by_col['ORIGINAL_ROW'][$i_row] = $i_row; // Track ORIGINAL row number during sorting.
   foreach($onerow as $s_column => $m_value) {
    $allqry_by_col[$s_column][$i_row] = is_string($m_value) ? strtolower($m_value) : $m_value; // Use lower case for sorting.
   }
  }
 return $allqry_by_col;
 }
// -------------------------------------------

Oh! oh! oh! Looks nice!!!!
I think I should get an "Assissted by " credit on that page!

Well done!
I would make 1 small change.

<a href="?faction=Heroes&server=Champion&sort=nameasc"><img src="images/sort_asc_gray.gif" title="Sort Name Ascending" border="0"></a>&nbsp;
<a href="?faction=Heroes&server=Champion&sort=namedesc"><img src="images/sort_des_gray.gif" title="Sort Name Descending" border="0"></a>

Remove the blank space around the image. This should remove the odd looking green underline, which underlines nothing.
Odd.  I integrated your code into my code.
This is the original HTML...

<td class="header">Rank</td>            <td class="header">
             <a href="?faction=Villains&server=Champion&sort=nameasc">
                   <img src="images/sort_asc_gray.gif" title="Sort Name Ascending" border="0">

             </a>&nbsp;
             <a href="?faction=Villains&server=Champion&sort=namedesc">
                   <img src="images/sort_des_gray.gif" title="Sort Name Descending" border="0">
             </a>
             Name
            </td>
http://badge-whore.com/players_get_data.php

<?php
 session_start();
 include("header.php");
// -------------------------------------------
 function getcache ($cachefile) {
  require($cachefile);
  $allqry_by_col = array();
  foreach($allqry as $i_row => &$onerow) {
   $allqry_by_col['ORIGINAL_ROW'][$i_row] = $i_row; // Track ORIGINAL row number during sorting.
   foreach($onerow as $s_column => $m_value) {
    $allqry_by_col[$s_column][$i_row] = is_string($m_value) ? strtolower($m_value) : $m_value; // Use lower case for sorting.
   }
  }
  return $allqry_by_col;
 }
// -------------------------------------------

 include("sqlauth.inc");
 $sql="SELECT * from users WHERE user_email=\"".$_SESSION['email']."\"";
 $result = mysql_query($sql,$connect) or die(mysql_error());
 $row = mysql_fetch_array($result);
 $privs = $row['user_priv'];
 $sql="SELECT COUNT(badge_type) AS \"count\" FROM badge_types";
 $result = mysql_query($sql,$connect) or die(mysql_error());
 $row = mysql_fetch_array($result);
 $typecount=$row['count'];
 $sql="SELECT * FROM verify_req";
 $result = mysql_query($sql,$connect) or die(mysql_error());
 $ver = mysql_fetch_array($result);
 
 if ($row['user_priv']>=1) {
       $i_delay = 300;
 }
 else {
       $i_delay = 300; //FIX THIS BEFORE LIVE ROLLOUT
 }

 $data_file='./cache/players_data.txt';

 $sortcol1='count';
 $sortcol1by=SORT_DESC;
 $sortcol2='char_name';
 $sortcol2by=SORT_ASC;
 if (isset($_GET['sort'])) {
  switch ($_GET['sort']) {
   case "nameasc" :
   case "namedesc" :
    $sortcol1 = 'char_name';
    $sortcol2 = 'count';
    $sortcol1by = ($_GET['sort'] == 'namedesc') ? SORT_DESC : SORT_ASC;
    $sortcol2by = SORT_DESC;
    $sortcol1on = SORT_STRING;
    $sortcol2on = SORT_NUMERIC;
    break;
  }
 }

 if (file_exists($data_file) && (filemtime($data_file) >= (time() - $i_delay))) {
  echo "Cache";
  $fullqry_by_col = getcache('./cache/players_data.txt');
  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);
 }
 else {
  echo "Refresh";
  require("players_cache.php");
  $fullqry_by_col = getcache('./cache/players_data.txt');
  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);
 }

 $sql="SELECT server_name FROM servers";
 $servers = mysql_query($sql,$connect) or die(mysql_error());
?>

 <p class="size20">Statistics of All Badge Whores - This data is updated every 5 minutes<p>
      <form name="players" method="post" action="players_process.php">
       <nobr>
            Set Query:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            Alignment:&nbsp;
            <select name="faction">
                  
<?php
 if ($_GET['faction']=="Heroes") {
       echo "<option selected=\"selected\">Heroes</option>\n<option>Villains</option>\n<option>All</option>\n";
 }
 elseif ($_GET['faction']=="Villains") {
       echo "<option>Heroes</option>\n<option selected=\"selected\">Villains</option>\n<option>All</option>\n";
 }
 else {
       echo "<option>Heroes</option>\n<option>Villains</option>\n<option selected=\"selected\">All</option>\n";
 }
?>

    </select>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Server:&nbsp;
            <select name="server">
                  
<?php
 while ($row = mysql_fetch_array($servers)) {
       if ($gotserver==$row['server_name']) {
             echo "<option selected=\"selected\">".$gotserver."</option>\n";
       }
       else {
             echo "<option>".$row['server_name']."</option>\n";
       }
 }
 if ($gotserver=="All") {
       echo "<option selected=\"selected\">All</option>\n";
 }
 else {
       echo "<option>All</option>\n";
 }
?>

    </select>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <input type="hidden" name="sort" value="<?=$_GET['sort']?>">
            <input type="submit" name="Submit" value="Submit">
       </form>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
       <a href="?">Clear Query</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      </nobr>
      <table width="100%">
       <tr>
      <?php
 if (($_GET['sort']<>"nameasc") AND ($_GET['sort']<>"namedesc")) {
       echo "<td class=\"header\">Rank</td>";
 }
?>
            <td class="header">
             <a href="?faction=<?php echo $_GET['faction'];?>&server=<?php echo $_GET['server'];?>&sort=nameasc">
                   <img src="images/sort_asc_gray.gif" title="Sort Name Ascending" border="0">
             </a>
             <a href="?faction=<?php echo $_GET['faction'];?>&server=<?php echo $_GET['server'];?>&sort=namedesc">
                   <img src="images/sort_des_gray.gif" title="Sort Name Descending" border="0">
             </a>
             Name
            </td>       
<?php
 if (($_GET['faction']=="All") OR ($_GET['faction']=="")) {
       echo "<td class=\"header\">Alignment</td>\n";
 }      
 echo "<td class=\"header\">Level</td>\n";
 if (($_GET['server']=="All") OR ($_GET['server']=="")) {
       echo "<td class=\"header\">Server</td>";
 }
?>
            <td class="header">Total Count</td>
            <td class="header">Verified</td>
            <td class="header">Date</td>
            <td class="header"></td>
            <td class="header"></td>
            <td class="header"></td>
            <td class="header"></td>
            <td class="header"></td>
            <td class="header"></td>
       </tr>
<?php
 $rank = 1;
 $displayrank = 1;
 $previoustotal = 0;
 foreach($fullqry_by_col['ORIGINAL_ROW'] as $i_sorted_row => $i_original_row)  {  // use the sorted original row "column" to map to original data.    
  echo "<tr>";
  if (($_GET['sort']<>"nameasc") AND ($_GET['sort']<>"namedesc")) {
   if ($fullqry[$i_original_row]['count']>=$previoustotal) {
        echo "<td>".$displayrank."</td>\n";
   }
   else {
    echo "<td>".$i_sorted_row."</td>\n";
        $displayrank=$i_sorted_row+1;
   }
   $previoustotal=$fullqry[$i_original_row]['count'];
  }
       echo "<td>\n<a class=\"none\" href=\"view_player.php?id=".$fullqry[$i_original_row]['char_id']."\"><font class=\"".$fullqry[$i_original_row]['faction']."_12\">".$fullqry[$i_original_row]['char_name']."</font></a></td>";
  if (($_GET['faction']=="All") OR ($_GET['faction']=="")) {
        if ($fullqry[$i_original_row]['char_faction']=="H") {
              echo "<td class=\"h_12\">Hero</td>\n";
        }
        else {
              echo "<td class=\"v_12\">Villain</td>\n";
        }
  }      
  echo "<td>".$fullqry[$i_original_row]['char_lvl']."</td>\n";
  if (($_GET['server']=="All") OR ($_GET['server']=="")) {
        echo "<td>".$fullqry[$i_original_row]['server_name']."</td>\n";
  }
  echo "<td>".$fullqry[$i_original_row]['count']."</td>";
  echo "<td>";
  if ($fullqry[$i_original_row]['faction']=="H") {
        $verfact=$ver['h'];
  }
  else {
        $verfact=$ver['v'];
  }
  if ($fullqry[$i_original_row]['ver_amt']<>"") {
        echo "<font class=\"verified\">".$row['ver_amt']."</font></td>\n";
        echo "<td><font class=\"verified\">".$row['ver_date']."</font></td>\n";
  }
  elseif ($fullqry[$i_original_row]['count'] < $verfaction) {
        echo "<font class=\"verifiednr\">Not Req'd</font></td>\n";
        echo "<td><font class=\"verifiednr\">Not Req'd</font></td>\n";
  }
  else {
   if ($fullqry[$i_original_row]['priv']==1) {
         echo "PRIVATE</td><td></td>\n";
   }
   else {
         echo "</td><td></td>\n";
   }
  }
  $charserver = "char_badges_".$fullqry[$i_original_row]['char_server'];
      for ($i=1;$i<=$typecount;$i++) {
   $sql = "SELECT COUNT(`badge_type`) AS \"count\" FROM ";
    $sql .= $charserver.", badges WHERE char_id=" . $fullqry[$i_original_row]['char_id'];
    $sql .= " AND ".$charserver.".badge_id=badges.badge_id AND badge_type=".$i;
    $sql .= " GROUP BY badge_type";
   $result2 = mysql_query($sql,$connect) or die(mysql_error());
   $row2 = mysql_fetch_array($result2); //50
   
   $sql = "SELECT * FROM badge_types WHERE badge_type=".$i;
   $resultb = mysql_query($sql,$connect) or die(mysql_error());
   $rowb = mysql_fetch_array($resultb); //50
   echo "<td class=\"".$rowb['url_desc']."\">";
   
   if (empty($fullqry[$i_original_row]['count'])) {
    echo "0";
   }
   else {
         echo $fullqry[$i_original_row]['count'];
   }
   echo "</td>";
  }
  echo "</tr>";
 }
 echo "</table>";             
 mysql_close($connect);
 include("footer.php");
?>
Oddly enough - it works if it has to refresh the data - it does not if it has to fetch it from cache.
         <td class="header">
           <a href="?faction=<?php echo $_GET['faction'];?>&server=<?php echo $_GET['server'];?>&sort=nameasc">
                <img src="images/sort_asc_gray.gif" title="Sort Name Ascending" border="0">
           </a>
           <a href="?faction=<?php echo $_GET['faction'];?>&server=<?php echo $_GET['server'];?>&sort=namedesc">
                <img src="images/sort_des_gray.gif" title="Sort Name Descending" border="0">
           </a>
           Name
          </td>

THe blank lines around the images cause the space and the underline to appear. You have to remove them.

          <td class="header">
           <a href="?faction=<?php echo $_GET['faction'];?>&server=<?php echo $_GET['server'];?>&sort=nameasc"><img src="images/sort_asc_gray.gif" title="Sort Name Ascending" border="0"></a>&nbsp;
           <a href="?faction=<?php echo $_GET['faction'];?>&server=<?php echo $_GET['server'];?>&sort=namedesc"><img src="images/sort_des_gray.gif" title="Sort Name Descending" border="0"></a>
           Name
          </td>      

Did that. Now, for some reason, the page won't load.  It just sits, loading.
rollback the change and try again.

I don't think this is the issue though. Seems like there is some sort of timeout going on.
This is annoying - I rolled back the change - now it insta loads - a blank screen.
The page which you are grabbing the data from.

Is it your OWN page?
Turn on ALL error reporting and examine your logs. You will be getting SOME errors somewhere!
I'm not sure how to go about doing that.  I do not have SSH access to this host.
<?php
error_reporting(E_ALL);
....

?>
ah, that's easy enough.

Unfortunately, I am on a shared server, so I don't think I can get to the log files - I can't seem to find them, anyway
I fixed the slowness - I forgot to simulate LIMIT.

I am still getting the error that only occurs during cache - everything works during refresh.  The error seems to indicate a null value for $fullqry.
How do you see the null value? As a PHP error?
Error (only appears when getting cached data): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AND char_badges_.badge_id=badges.badge_id AND badge_type=1 GROUP BY badge_type' at line 1

There is only one query that could possibly be referring to:

 for ($i=1;$i<=$typecount;$i++) {
  $sql = "SELECT COUNT(`badge_type`) AS \"count\" FROM ";
   $sql .= $charserver.", badges WHERE char_id=" . $fullqry[$i_original_row]['char_id'];
   $sql .= " AND ".$charserver.".badge_id=badges.badge_id AND badge_type=".$i;
   $sql .= " GROUP BY badge_type";
  $result2 = mysql_query($sql,$connect) or die(mysql_error());
  $row2 = mysql_fetch_array($result2); //50

You can also see it in action at http://www.badge-whore.com/players_get_data.php

Below the header, I have a diagnostic echo that states whether you are seeing refreshed data or cached data.
Can you supply the ENTIRE code again?

$i_original_row is probably WAY wrong!!!
<?php
 session_start();
 include("header.php");
// -------------------------------------------
 function getcache ($cachefile) {
  require($cachefile);
  $allqry_by_col = array();
  foreach($fullqry as $i_row => &$onerow) {
   $allqry_by_col['ORIGINAL_ROW'][$i_row] = $i_row; // Track ORIGINAL row number during sorting.
   foreach($onerow as $s_column => $m_value) {
    $allqry_by_col[$s_column][$i_row] = is_string($m_value) ? strtolower($m_value) : $m_value; // Use lower case for sorting.
   }
  }
  return $allqry_by_col;
 }
// -------------------------------------------

 include("sqlauth.inc");
 $db = mysql_select_db($dbname,$connect);
 $connect = mysql_connect($host,$account,$password);
 $sql="SELECT * from users WHERE user_email=\"".$_SESSION['email']."\"";
 $result = mysql_query($sql,$connect) or die(mysql_error());
 $row = mysql_fetch_array($result);
 $privs = $row['user_priv'];
 $sql="SELECT COUNT(badge_type) AS \"count\" FROM badge_types";
 $result = mysql_query($sql,$connect) or die(mysql_error());
 $row = mysql_fetch_array($result);
 $typecount=$row['count'];
 $sql="SELECT * FROM verify_req";
 $result = mysql_query($sql,$connect) or die(mysql_error());
 $ver = mysql_fetch_array($result);
 $gotserver=$_GET['server'];
 switch ($gotserver) {
       case "":
       case "All":
        $server = "All";
   break;
  default :
   $sql="SELECT * FROM servers WHERE server_name=\"".$gotserver."\"";
   $result = mysql_query($sql,$connect) or die(mysql_error());
   $row = mysql_fetch_array($result);
   $server = "char_badges_".$row['server_id'];
  }
 
 if ($row['user_priv']>=1) {
       $i_delay = 30;
 }
 else {
       $i_delay = 60;
 }

 $data_file='./cache/players_data.txt';

 $sortcol1='count';
 $sortcol1by=SORT_DESC;
 $sortcol2='char_name';
 $sortcol2by=SORT_ASC;
 if (isset($_GET['sort'])) {
  switch ($_GET['sort']) {
   case "nameasc" :
   case "namedesc" :
    $sortcol1 = 'char_name';
    $sortcol2 = 'count';
    $sortcol1by = ($_GET['sort'] == 'namedesc') ? SORT_DESC : SORT_ASC;
    $sortcol2by = SORT_DESC;
    $sortcol1on = SORT_STRING;
    $sortcol2on = SORT_NUMERIC;
    break;
  }
 }

 if (file_exists($data_file) && (filemtime($data_file) >= (time() - $i_delay))) {
  echo "Cache";
  $fullqry_by_col = getcache('./cache/players_data.txt');
  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);
 }
 else {
  echo "Refresh";
  require("players_cache.php");
  $fullqry_by_col = getcache('./cache/players_data.txt');
  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);
 }

 $sql="SELECT server_name FROM servers";
 $servers = mysql_query($sql,$connect) or die(mysql_error());
?>

 <p class="size20">Statistics of All Badge Whores - This data is updated every 5 minutes<p>
      <form name="players" method="post" action="players_process.php">
       <nobr>
            Set Query:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            Alignment:&nbsp;
            <select name="faction">
                  
<?php
 if ($_GET['faction']=="Heroes") {
       echo "<option selected=\"selected\">Heroes</option>\n<option>Villains</option>\n<option>All</option>\n";
 }
 elseif ($_GET['faction']=="Villains") {
       echo "<option>Heroes</option>\n<option selected=\"selected\">Villains</option>\n<option>All</option>\n";
 }
 else {
       echo "<option>Heroes</option>\n<option>Villains</option>\n<option selected=\"selected\">All</option>\n";
 }
?>

    </select>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Server:&nbsp;
            <select name="server">
                  
<?php
 while ($row = mysql_fetch_array($servers)) {
       if ($f==$row['server_name']) {
             echo "<option selected=\"selected\">".$gotserver."</option>\n";
       }
       else {
             echo "<option>".$row['server_name']."</option>\n";
       }
 }
 if ($gotserver=="All") {
       echo "<option selected=\"selected\">All</option>\n";
 }
 else {
       echo "<option>All</option>\n";
 }
?>

    </select>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <input type="hidden" name="sort" value="<?=$_GET['sort']?>">
            <input type="submit" name="Submit" value="Submit">
       </form>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
       <a href="?">Clear Query</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      </nobr>
      <table width="100%">
       <tr>
      <?php
 if (($_GET['sort']<>"nameasc") AND ($_GET['sort']<>"namedesc")) {
       echo "<td class=\"header\">Rank</td>";
 }
?>
            <td class="header">
             <a href="?faction=<?php echo $_GET['faction'];?>&server=<?php echo $_GET['server'];?>&sort=nameasc">
                   <img src="images/sort_asc_gray.gif" title="Sort Name Ascending" border="0"></a>
                   <a href="?faction=<?php echo $_GET['faction'];?>&server=<?php echo $_GET['server'];?>&sort=namedesc">
                   <img src="images/sort_des_gray.gif" title="Sort Name Descending" border="0"></a>
             Name
            </td>       
<?php
 if (($_GET['faction']=="All") OR ($_GET['faction']=="")) {
       echo "<td class=\"header\">Alignment</td>\n";
 }      
 echo "<td class=\"header\">Level</td>\n";
 if (($_GET['server']=="All") OR ($_GET['server']=="")) {
       echo "<td class=\"header\">Server</td>";
 }
?>
            <td class="header">Total Count</td>
            <td class="header">Verified</td>
            <td class="header">Date</td>
            <td class="header"></td>
            <td class="header"></td>
            <td class="header"></td>
            <td class="header"></td>
            <td class="header"></td>
            <td class="header"></td>
       </tr>
<?php
 $rank = 1;
 $displayrank = 1;
 $previoustotal = 0;
 foreach($fullqry_by_col['ORIGINAL_ROW'] as $i_sorted_row => $i_original_row)  {  // use the sorted original row "column" to map to original data.    
  if ($i_sorted_row>200) {
        break;
  }
  echo "<tr>";
  if (($_GET['sort']<>"nameasc") AND ($_GET['sort']<>"namedesc")) {
   if ($fullqry[$i_original_row]['count']>=$previoustotal) {
        echo "<td>".$displayrank."</td>\n";
   }
   else {
    echo "<td>".($i_sorted_row+1)."</td>\n";
        $displayrank=$i_sorted_row+1;
   }
   $previoustotal=$fullqry[$i_original_row]['count'];
  }
       //echo "<td>{$fullqry[$i_original_row]['char_name']}</td>";
       echo "<td>\n<a class=\"none\" href=\"view_player.php?id=".$fullqry[$i_original_row]['char_id']."\"><font class=\"".$fullqry[$i_original_row]['faction']."_12\">".$fullqry[$i_original_row]['char_name']."</font></a></td>";
  if (($_GET['faction']=="All") OR ($_GET['faction']=="")) {
        if ($fullqry[$i_original_row]['char_faction']=="H") {
              echo "<td class=\"h_12\">Hero</td>\n";
        }
        else {
              echo "<td class=\"v_12\">Villain</td>\n";
        }
  }      
  echo "<td>".$fullqry[$i_original_row]['char_lvl']."</td>\n";
  if (($_GET['server']=="All") OR ($_GET['server']=="")) {
        echo "<td>".$fullqry[$i_original_row]['server_name']."</td>\n";
  }
  echo "<td>".$fullqry[$i_original_row]['count']."</td>";
  echo "<td>";
  if ($fullqry[$i_original_row]['faction']=="H") {
        $verfact=$ver['h'];
  }
  else {
        $verfact=$ver['v'];
  }
  if ($fullqry[$i_original_row]['ver_amt']<>"") {
        echo "<font class=\"verified\">".$row['ver_amt']."</font></td>\n";
        echo "<td><font class=\"verified\">".$row['ver_date']."</font></td>\n";
  }
  elseif ($fullqry[$i_original_row]['count'] < $verfaction) {
        echo "<font class=\"verifiednr\">Not Req'd</font></td>\n";
        echo "<td><font class=\"verifiednr\">Not Req'd</font></td>\n";
  }
  else {
   if ($fullqry[$i_original_row]['priv']==1) {
         echo "PRIVATE</td><td></td>\n";
   }
   else {
         echo "</td><td></td>\n";
   }
  }
  $charserver = "char_badges_".$fullqry[$i_original_row]['char_server'];
      for ($i=1;$i<=$typecount;$i++) {
   $sql = "SELECT COUNT(`badge_type`) AS \"count\" FROM ";
    $sql .= $charserver.", badges WHERE char_id=" . $fullqry[$i_original_row]['char_id'];
    $sql .= " AND ".$charserver.".badge_id=badges.badge_id AND badge_type=".$i;
    $sql .= " GROUP BY badge_type";
   $result2 = mysql_query($sql,$connect) or die(mysql_error());
   $row2 = mysql_fetch_array($result2); //50
   
   $sql = "SELECT * FROM badge_types WHERE badge_type=".$i;
   $resultb = mysql_query($sql,$connect) or die(mysql_error());
   $rowb = mysql_fetch_array($resultb); //50
   echo "<td class=\"".$rowb['url_desc']."\">";
   
   if (empty($fullqry[$i_original_row]['count'])) {
    echo "0";
   }
   else {
         echo $fullqry[$i_original_row]['count'];
   }
   echo "</td>";
  }
  echo "</tr>";
 }
 echo "</table>";             
 mysql_close($connect);
 include("footer.php");
?>
if (file_exists($data_file) && (filemtime($data_file) >= (time() - $i_delay))) {
  echo "Cache";
  $fullqry_by_col = getcache('./cache/players_data.txt');
  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);
 }
 else {
  echo "Refresh";
  require("players_cache.php");
  $fullqry_by_col = getcache('./cache/players_data.txt');
  array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);
 }

can be ...

if (file_exists($data_file) && (filemtime($data_file) >= (time() - $i_delay)))
      {
      }
else
      {
      require("players_cache.php");
      echo "Refresh";
      }
echo "Cache";
$fullqry_by_col = getcache('./cache/players_data.txt');
array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);


No change
if (!file_exists($data_file) || (filemtime($data_file) < (time() - $i_delay)))
     {
     require("players_cache.php");
     echo "Refresh";
     }

even better.
<?php
session_start();
include("header.php");

// -------------------------------------------
function getcache ($cachefile)
      {
      require($cachefile);
      $allqry_by_col = array();
      foreach($fullqry as $i_row => &$onerow)
            {
            $allqry_by_col['ORIGINAL_ROW'][$i_row] = $i_row; // Track ORIGINAL row number during sorting.
            foreach($onerow as $s_column => $m_value)
                  {
                  $allqry_by_col[$s_column][$i_row] = is_string($m_value) ? strtolower($m_value) : $m_value; // Use lower case for sorting.
                  }
            }
      return $allqry_by_col;
      }
// -------------------------------------------

include("sqlauth.inc");
$db = mysql_select_db($dbname,$connect);
$connect = mysql_connect($host,$account,$password);
$sql = "SELECT * from users WHERE user_email='{$_SESSION['email']}'";
$result = mysql_query($sql,$connect) or die(mysql_error());
$row = mysql_fetch_array($result);
$privs = $row['user_priv'];
$sql = "SELECT COUNT(badge_type) AS 'count' FROM badge_types";
$result = mysql_query($sql,$connect) or die(mysql_error());
$row = mysql_fetch_array($result);
$typecount = $row['count'];
$sql = "SELECT * FROM verify_req";
$result = mysql_query($sql,$connect) or die(mysql_error());
$ver = mysql_fetch_array($result);
$gotserver = $_GET['server'];

switch ($gotserver)
      {
      case "":
      case "All":
            $server = "All";
            break;
      default :
            $sql = "SELECT * FROM servers WHERE server_name='{$gotserver}'";
            $result = mysql_query($sql,$connect) or die(mysql_error());
            $row = mysql_fetch_array($result);
            $server = "char_badges_{$row['server_id']}";
      }
 
if ($row['user_priv'] >= 1)
      {
      $i_delay = 30;
      }
else
      {
      $i_delay = 60;
      }

$data_file='./cache/players_data.txt';

$sortcol1='count';
$sortcol1by=SORT_DESC;
$sortcol2='char_name';
$sortcol2by=SORT_ASC;
if (isset($_GET['sort']))
      {
      switch ($_GET['sort'])
            {
            case "nameasc" :
            case "namedesc" :
                  $sortcol1 = 'char_name';
                  $sortcol2 = 'count';
                  $sortcol1by = ($_GET['sort'] == 'namedesc') ? SORT_DESC : SORT_ASC;
                  $sortcol2by = SORT_DESC;
                  break;
            }
      }

if (!file_exists($data_file) || (filemtime($data_file) < (time() - $i_delay)))
     {
     require("players_cache.php");
     echo "Refresh";
     }
$fullqry_by_col = getcache('./cache/players_data.txt');
array_multisort($fullqry_by_col[$sortcol1], $sortcol1by, $fullqry_by_col[$sortcol2], $sortcol2by, $fullqry_by_col['ORIGINAL_ROW']);


$sql="SELECT server_name FROM servers";
$servers = mysql_query($sql,$connect) or die(mysql_error());

$s_selected = ' select="selected"';
$s_selected_heroes = isset($_GET['faction']) && ($_GET['faction'] == 'Heroes' ? $s_selected : '';
$s_selected_villans = isset($_GET['faction']) && ($_GET['faction'] == 'Villians' ? $s_selected : '';
$s_selected_all = !isset($_GET['faction']) || !in_array($_GET['faction'], array('Heroes', 'Villans')) ? $_selected : '';

$s_server_options = '';
while ($row = mysql_fetch_array($servers))
      {
      $s_selected_server = ($_GET['server'] == $row['server_name']) ? $s_selected : '';
      $s_server_options .= "<option{$s_selected_server}>{$row['server_name']}</option>\n";
      }
$s_selected_server_all = ($gotserver == "All" ? $s_selected : '');

$s_output = <<< END_HTML
<p class="size20">Statistics of All Badge Whores - This data is updated every 5 minutes<p>
<form name="players" method="post" action="players_process.php">
      <nobr>
      Set Query:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      Alignment:&nbsp;
      <select name="faction">
            <option{$s_selected_heroes}>Heroes</option>
            <option{$s_selected_villans}>Villains</option>
            <option{$s_selected_all}>All</option>
      </select>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Server:&nbsp;
        <select name="server">
            {$s_server_options}
            <option{$s_selected_server_all}>All</option>\n";
      </select>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
      <input type="hidden" name="sort" value="<?=$_GET['sort']?>">
      <input type="submit" name="Submit" value="Submit">
      <input type="reset" name="Reset" value="Reset">
          </nobr>
</form>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href="?">Clear Query</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
END_HTML;


// Got to here on the rewrite. Hopefully you can see a way to constructing better HTML rather than lots of <?php ?> all over the place.

?>

<table width="100%">
      <tr>
     <?php
 if (($_GET['sort']<>"nameasc") AND ($_GET['sort']<>"namedesc")) {
      echo "<td class=\"header\">Rank</td>";
 }
?>
          <td class="header">
           <a href="?faction=<?php echo $_GET['faction'];?>&server=<?php echo $_GET['server'];?>&sort=nameasc">
                <img src="images/sort_asc_gray.gif" title="Sort Name Ascending" border="0"></a>
                <a href="?faction=<?php echo $_GET['faction'];?>&server=<?php echo $_GET['server'];?>&sort=namedesc">
                <img src="images/sort_des_gray.gif" title="Sort Name Descending" border="0"></a>
           Name
          </td>      
<?php
 if (($_GET['faction']=="All") OR ($_GET['faction']=="")) {
      echo "<td class=\"header\">Alignment</td>\n";
 }    
 echo "<td class=\"header\">Level</td>\n";
 if (($_GET['server']=="All") OR ($_GET['server']=="")) {
      echo "<td class=\"header\">Server</td>";
 }
?>
          <td class="header">Total Count</td>
          <td class="header">Verified</td>
          <td class="header">Date</td>
          <td class="header"></td>
          <td class="header"></td>
          <td class="header"></td>
          <td class="header"></td>
          <td class="header"></td>
          <td class="header"></td>
      </tr>
<?php
 $rank = 1;
 $displayrank = 1;
 $previoustotal = 0;
 foreach($fullqry_by_col['ORIGINAL_ROW'] as $i_sorted_row => $i_original_row)  {  // use the sorted original row "column" to map to original data.    
  if ($i_sorted_row>200) {
       break;
  }
  echo "<tr>";
  if (($_GET['sort']<>"nameasc") AND ($_GET['sort']<>"namedesc")) {
   if ($fullqry[$i_original_row]['count']>=$previoustotal) {
       echo "<td>".$displayrank."</td>\n";
   }
   else {
    echo "<td>".($i_sorted_row+1)."</td>\n";
       $displayrank=$i_sorted_row+1;
   }
   $previoustotal=$fullqry[$i_original_row]['count'];
  }
      //echo "<td>{$fullqry[$i_original_row]['char_name']}</td>";
      echo "<td>\n<a class=\"none\" href=\"view_player.php?id=".$fullqry[$i_original_row]['char_id']."\"><font class=\"".$fullqry[$i_original_row]['faction']."_12\">".$fullqry[$i_original_row]['char_name']."</font></a></td>";
  if (($_GET['faction']=="All") OR ($_GET['faction']=="")) {
       if ($fullqry[$i_original_row]['char_faction']=="H") {
            echo "<td class=\"h_12\">Hero</td>\n";
       }
       else {
            echo "<td class=\"v_12\">Villain</td>\n";
       }
  }    
  echo "<td>".$fullqry[$i_original_row]['char_lvl']."</td>\n";
  if (($_GET['server']=="All") OR ($_GET['server']=="")) {
       echo "<td>".$fullqry[$i_original_row]['server_name']."</td>\n";
  }
  echo "<td>".$fullqry[$i_original_row]['count']."</td>";
  echo "<td>";
  if ($fullqry[$i_original_row]['faction']=="H") {
       $verfact=$ver['h'];
  }
  else {
       $verfact=$ver['v'];
  }
  if ($fullqry[$i_original_row]['ver_amt']<>"") {
       echo "<font class=\"verified\">".$row['ver_amt']."</font></td>\n";
       echo "<td><font class=\"verified\">".$row['ver_date']."</font></td>\n";
  }
  elseif ($fullqry[$i_original_row]['count'] < $verfaction) {
       echo "<font class=\"verifiednr\">Not Req'd</font></td>\n";
       echo "<td><font class=\"verifiednr\">Not Req'd</font></td>\n";
  }
  else {
   if ($fullqry[$i_original_row]['priv']==1) {
        echo "PRIVATE</td><td></td>\n";
   }
   else {
        echo "</td><td></td>\n";
   }
  }
  $charserver = "char_badges_".$fullqry[$i_original_row]['char_server'];
     for ($i=1;$i<=$typecount;$i++) {
   $sql = "SELECT COUNT(`badge_type`) AS \"count\" FROM ";
    $sql .= $charserver.", badges WHERE char_id=" . $fullqry[$i_original_row]['char_id'];
    $sql .= " AND ".$charserver.".badge_id=badges.badge_id AND badge_type=".$i;
    $sql .= " GROUP BY badge_type";
   $result2 = mysql_query($sql,$connect) or die(mysql_error());
   $row2 = mysql_fetch_array($result2); //50
   
   $sql = "SELECT * FROM badge_types WHERE badge_type=".$i;
   $resultb = mysql_query($sql,$connect) or die(mysql_error());
   $rowb = mysql_fetch_array($resultb); //50
   echo "<td class=\"".$rowb['url_desc']."\">";
   
   if (empty($fullqry[$i_original_row]['count'])) {
    echo "0";
   }
   else {
        echo $fullqry[$i_original_row]['count'];
   }
   echo "</td>";
  }
  echo "</tr>";
 }
 echo "</table>";          
 mysql_close($connect);
 include("footer.php");
?>
Yes, I clean my files up and make them pretty, and efficient after I get them working.  When they are not, I try to make the code as /unefficient/ as possible - the expanded lines help me diagnose problems.

I am still getting error when retrieving cached data.
Do you have any imposed limits on memory usage?
Not that I know of.
Maybe it would be worth cleaning the code now anyway. Modularize it.

Do make sure you have FULL error_reporting(E_ALL); on.
I do.  The only error I get on the screen is the one I have relayed.  If I have access to the PHP log files, it is not anywhere I have found.

I don't understand why memory would be an issue - both refreshing and caching require the full array to be in memory.  Refreshing just puts the data there by query and caching just reads it in from a file.  In fact, I would think refreshing would be more memory effecient.

It seems like somehow, during caching, the $fullqry variable isn't being properly set.
It may be that your script has only so much parsing time. Especially with a LARGE data array.

Try adding the following debug.

At the top of the script ...

if (file_exists('./backup.txt'))
 {
 unlink('./backup.txt');
 }

At the end of the script (and the line will move up as you progress with the debug) ...

file_put_contents('./backup.txt', '<?php $a_backup = ' . var_export($fullqry, True) . '; ?>');

Now run your code. When you get a partial page, see what the backup file contains. See if it contains ANYTHING.

The idea is that we move the backup line up until we get a consistent behaviour (i.e. broken page but valid backup file).
I will begin trying that.  I get a partial page anyway.  Linkage: http://badge-whore.com/players_get_data.php?
Take a look at the view-source you are creating.

You have 2 <head> tags. One at the top and one at the bottom.

Not a good idea.
This is a cleaned view-source ...

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>
      Badge-Whore.com
    </title>
    <link rel="SHORTCUT ICON" href="favicon.ico" />
<style type="text/css">
/*<![CDATA[*/
@import "format.css";
/*]]>*/
</style>
    <meta name="description" content="City of Heroes and Villains Badge Collecting" />
    <meta name="Content-Language" content="en" />
    <meta http-equiv="Pragma" content="no-cache" />

    <meta http-equiv="Expires" content="-1" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Expires" content="-1" />
  </head>
  <body>
    <table>
      <tr>
        <td class="none">
          <a name="top" id="top"><img src="images/banner.png" border="0" /></a>

        </td>
        <td class="nonetop">
          <a href="view_badge_bytype.php?type=1" class="exploration_11">Exploration</a><br />
          <a href="view_badge_bytype.php?type=2" class="history_11">History</a><br />
          <a href="view_badge_bytype.php?type=3" class="accomplishment_11">Accomplishments</a><br />
          <a href="view_badge_bytype.php?type=4" class="achievement_11">Achievements</a><br />
          <a href="view_badge_bytype.php?type=5" class="accolade_11">Accolades</a><br />

          <a href="view_badge_bytype.php?type=6" class="gladiator_11">Gladiators</a><br />
          <br />
        </td>
      </tr>
      <tr>
        <td class="none">
          <a href="index.php" class="white_12">&nbsp;<u>Home</u>&nbsp;</a> <a href="welcome.php" class="green">&nbsp;<u>Your Badge Whores</u>&nbsp;</a> <a href="players.php" class="white_12">&nbsp;<u>Badge Whore Stats</u>&nbsp;</a> <a href="http://www.vidiotmaps.com/home.html" class="whitealt_12">&nbsp;<u>VidiotMaps.com</u>&nbsp;</a> <a href="http://forum.coh-elite.org/" class="white_12">&nbsp;<u>Forums</u>&nbsp;</a> <a href="logout.php" class="white_12">&nbsp;<u>Logout</u>&nbsp;</a>

        </td>
      </tr>
    </table>
    <p class="size20">
      Statistics of All Badge Whores - This data is updated every 5 minutes
    </p>
    <form name="players" method="post" action="players_process.php" id="players">
      Set&nbsp;Query:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Alignment:&nbsp;&nbsp;<select name="faction">

        <option>
          Heroes
        </option>
        <option selected="selected">
          Villains
        </option>
        <option>
          All
        </option>
      </select>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Server:&nbsp;&nbsp;<select name="server">

        <option>
          Champion
        </option>
        <option>
          Defiant
        </option>
        <option>
          Freedom
        </option>
        <option>

          Guardian
        </option>
        <option>
          Infinity
        </option>
        <option>
          Justice
        </option>
        <option>
          Liberty
        </option>

        <option>
          Pinnacle
        </option>
        <option>
          Protector
        </option>
        <option>
          Triumph
        </option>
        <option selected="selected">

          Union
        </option>
        <option>
          Victory
        </option>
        <option>
          Vigilance
        </option>
        <option>
          Virtue
        </option>

        <option>
          Zunkunft
        </option>
        <option>
          All
        </option>
      </select>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type="hidden" name="sort" value="" />&nbsp;<input type="submit" name="Submit" value="Submit" />
    </form>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="players.php">Clear Query</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    <table width="100%">

      <tr>
        <td class="header">
          Rank
        </td>
        <td class="header">
          <a href="?faction=Villains&amp;server=Union&amp;sort=nameasc"><img src="images/sort_asc_gray.gif" title="Sort Name Ascending" border="0" /></a> &nbsp; <a href="?faction=Villains&amp;server=Union&amp;sort=namedesc"><img src="images/sort_des_gray.gif" title="Sort Name Descending" border="0" /></a> Name
        </td>
        <td class="header">

          Level
        </td>
        <td class="header">
          Total Count
        </td>
        <td class="header">
          Verified
        </td>
        <td class="header">
          Date
        </td>

        <td class="header"></td>
        <td class="header"></td>
        <td class="header"></td>
        <td class="header"></td>
        <td class="header"></td>
        <td class="header"></td>
      </tr>
      <tr>
        <td>

          1
        </td>
        <td>
          <a class="none" href="view_player.php?id=40"><span>Black Jon Bludd</span></a>
        </td>
        <td>
          40
        </td>
        <td>

          215
        </td>
        <td></td>
        <td></td>
        <td class="exploration">
          42
        </td>
        <td class="history">
          3
        </td>

        <td class="accomplishment">
          18
        </td>
        <td class="achievement">
          93
        </td>
        <td class="accolade">
          8
        </td>
        <td class="gladiator">

          51
        </td>
      </tr>
      <tr>
        <td>
          2
        </td>
        <td>
          <a class="none" href="view_player.php?id=34"><span>Lord Kalistoh</span></a>

        </td>
        <td>
          39
        </td>
        <td>
          153
        </td>
        <td></td>
        <td></td>
        <td class="exploration">

          42
        </td>
        <td class="history">
          3
        </td>
        <td class="accomplishment">
          13
        </td>
        <td class="achievement">
          60
        </td>

        <td class="accolade">
          6
        </td>
        <td class="gladiator">
          29
        </td>
      </tr>
      <tr>
        <td>
          3
        </td>

        <td>
          <a class="none" href="view_player.php?id=1866"><span>Venador</span></a>
        </td>
        <td>
          40
        </td>
        <td>
          152
        </td>

        <td></td>
        <td></td>
        <td class="exploration">
          40
        </td>
        <td class="history">
          1
        </td>
        <td class="accomplishment">
          11
        </td>

        <td class="achievement">
          60
        </td>
        <td class="accolade">
          7
        </td>
        <td class="gladiator">
          33
        </td>
      </tr>

      <tr>
        <td>
          4
        </td>
        <td>
          <a class="none" href="view_player.php?id=1656"><span>Scarlet Vengeance</span></a>
        </td>
        <td>
          40
        </td>

        <td>
          151
        </td>
        <td></td>
        <td></td>
        <td class="exploration">
          40
        </td>
        <td class="history">
          3
        </td>

        <td class="accomplishment">
          14
        </td>
        <td class="achievement">
          53
        </td>
        <td class="accolade">
          8
        </td>
        <td class="gladiator">

          33
        </td>
      </tr>
      <tr>
        <td>
          5
        </td>
        <td>
          <a class="none" href="view_player.php?id=1661"><span>Zatanninja</span></a>

        </td>
        <td>
          39
        </td>
        <td>
          136
        </td>
        <td></td>
        <td></td>
        <td class="exploration">

          40
        </td>
        <td class="history">
          3
        </td>
        <td class="accomplishment">
          10
        </td>
        <td class="achievement">
          45
        </td>

        <td class="accolade">
          7
        </td>
        <td class="gladiator">
          31
        </td>
      </tr>
      <tr>
        <td>
          6
        </td>

        <td>
          <a class="none" href="view_player.php?id=1436"><span>Coffin Dodger</span></a>
        </td>
        <td>
          30
        </td>
        <td>
          118
        </td>

        <td></td>
        <td></td>
        <td class="exploration">
          38
        </td>
        <td class="history">
          3
        </td>
        <td class="accomplishment">
          11
        </td>

        <td class="achievement">
          37
        </td>
        <td class="accolade">
          5
        </td>
        <td class="gladiator">
          24
        </td>
      </tr>

      <tr>
        <td>
          7
        </td>
        <td>
          <a class="none" href="view_player.php?id=1139"><span>Lord Amber</span></a>
        </td>
        <td>
          34
        </td>

        <td>
          103
        </td>
        <td></td>
        <td></td>
        <td class="exploration">
          40
        </td>
        <td class="history">
          3
        </td>

        <td class="accomplishment">
          8
        </td>
        <td class="achievement">
          34
        </td>
        <td class="accolade">
          5
        </td>
        <td class="gladiator">

          13
        </td>
      </tr>
      <tr>
        <td>
          8
        </td>
        <td>
          <a class="none" href="view_player.php?id=1844"><span>Lord Viktor</span></a>

        </td>
        <td>
          25
        </td>
        <td>
          94
        </td>
        <td></td>
        <td></td>
        <td class="exploration">

          35
        </td>
        <td class="history">
          3
        </td>
        <td class="accomplishment">
          9
        </td>
        <td class="achievement">
          27
        </td>

        <td class="accolade">
          2
        </td>
        <td class="gladiator">
          18
        </td>
      </tr>
      <tr>
        <td>
          9
        </td>

        <td>
          <a class="none" href="view_player.php?id=1603"><span>Temera</span></a>
        </td>
        <td>
          22
        </td>
        <td>
          48
        </td>

        <td></td>
        <td></td>
        <td class="exploration">
          17
        </td>
        <td class="history">
          0
        </td>
        <td class="accomplishment">
          5
        </td>

        <td class="achievement">
          14
        </td>
        <td class="accolade">
          2
        </td>
        <td class="gladiator">
          10
        </td>
      </tr>

      <tr>
        <td>
          10
        </td>
        <td>
          <a class="none" href="view_player.php?id=1438"><span>Soviet Terror</span></a>
        </td>
        <td>
          13
        </td>

        <td>
          33
        </td>
        <td></td>
        <td></td>
        <td class="exploration">
          10
        </td>
        <td class="history">
          0
        </td>

        <td class="accomplishment">
          4
        </td>
        <td class="achievement">
          8
        </td>
        <td class="accolade">
          5
        </td>
        <td class="gladiator">

          6
        </td>
      </tr>
      <tr>
        <td>
          11
        </td>
        <td>
          <a class="none" href="view_player.php?id=925"><span>Skully The Skull</span></a>

        </td>
        <td>
          18
        </td>
        <td>
          5
        </td>
        <td></td>
        <td></td>
        <td class="exploration">

          0
        </td>
        <td class="history">
          0
        </td>
        <td class="accomplishment">
          4
        </td>
        <td class="achievement">
          0
        </td>

        <td class="accolade">
          1
        </td>
        <td class="gladiator">
          0
        </td>
      </tr>
      <tr>
        <td>
          12
        </td>

        <td>
          <a class="none" href="view_player.php?id=78"><span>Prue Halliwell</span></a>
        </td>
        <td>
          40
        </td>
        <td>
          4
        </td>

        <td></td>
        <td></td>
        <td class="exploration">
          0
        </td>
        <td class="history">
          0
        </td>
        <td class="accomplishment">
          0
        </td>

        <td class="achievement">
          0
        </td>
        <td class="accolade">
          4
        </td>
        <td class="gladiator">
          0
        </td>
      </tr>

    </table><br />
    <br />
    <a href="contact.php">Contact Us!</a><br />
    <br />
    <table>
      <tr>
        <td class="nonetop">
          <form action="https://www.paypal.com/cgi-bin/webscr" method="post">

            <input type="hidden" name="cmd" value="_xclick" /> <input type="hidden" name="business" value="bralston9@comcast.net" /> <input type="hidden" name="item_name" value="Badge-Whore.com Donation" /> <input type="image" src="https://www.paypal.com/images/x-click-but21.gif" name="submit" alt="Make payments with PayPal - it's fast, free and secure!" />
          </form>
        </td>
        <td class="nonetop">
          &nbsp;&nbsp;Please donate to keep this site running strong.
        </td>
      </tr>
    </table>

    <p>
      <span>Badge-Whore.com Hits:</span> <span>2299<br /></span>
    </p>
    <p class="bottom_statement">
      © 2006 VidiotMaps.com and Badge-Whore.com site design by The VidiotMaps Group. All rights reserved.<br />
      City of Heroes and City of Villains are registered trademarks of Cryptic Studios, Inc. and NCsoft Corporation.
    </p>

  </body>
</html>


Try to create this.
I figured it out.  During cache, $fullqry was only being referenced in the function, which lives in its own world.  It doesn't persist.  So I moved the function code in line, and it works.

Its slow - almost as slow as the refresh, but that's a topic for another question, I deem.

RQuadling, you have certainly earned these points, and my gratitude.  I have learned quite a bit of syntax, and will be better for it.
Oh, the two heads was on purpose - to defeat an IE bug.  Basically, IE ignores pragma rules if it wasn't planning on caching anyway.  And IE doesn't decide to cache until the page hits a certain threshold.  And since the pragma rules are in the top, IE almost ALWAY ignores them.  So another head in the bottom, and IE knows not to cache.  I know its not standards compliant, but such is the bane of making IE do what it is told.