Forum Moderators: coopster

Message Too Old, No Replies

Can this code be optimized?

A Guestbook with multiple pages

         

BlackDex

7:03 pm on Dec 29, 2004 (gmt 0)

10+ Year Member



Hello there ppl,

I Have coded the basics for an guestbook, but i wonderd if it is posible to optimize it or something?

I Use the phpBB/phpNuke database routine.
The 'Max_Guestbook_Records; contains the max entrys that should be shown per page.
The '$Guestbook_Start_Record' contains a value about wich record to start with for example 0 for the the latest entrys 0->4 and 5 will show entrys 5->10 etc..


<?php
global $db, $dbprefix, $Max_Guestbook_Records;

$sql = "SELECT * FROM ".$dbprefix."_guestbook";
$result = $db->sql_query($sql);

$Guestbook_Records = array();
while ( $Guestbook_Records[] = $db->sql_fetchrow($result) )
$Total_Guestbook_Records = count($Guestbook_Records);

$Guestbook_Start_Record = 0;
$Guestbook_Start = $Total_Guestbook_Records - $Guestbook_Start_Record - 1;
for ($i = 0; $i <= ($Max_Guestbook_Records - 1); $i++)
{
$r = $Guestbook_Start - $i;
print "Nr: $i<br />\nNaam: {$Guestbook_Records[$r][name]}<br />\nComment: {$Guestbook_Records[$r][comment]}<br /><br />\n";
}

?>

Thx in Advance

baze22

9:53 pm on Dec 29, 2004 (gmt 0)

10+ Year Member



Haven't tested this, but seems like it should work:
<?php 
global $db, $dbprefix, $Max_Guestbook_Records;

$sql = "SELECT * FROM ".$dbprefix."_guestbook";
$result = $db->sql_query($sql);

$Guestbook_Record = array();
while ( $Guestbook_Record = $db->sql_fetchrow($result) ) {
print "Nr: $i<br />\nNaam: {$Guestbook_Record[name]}<br />\nComment: {$Guestbook_Record[comment]}<br /><br />\n";
}
?>

baze

BlackDex

10:05 pm on Dec 29, 2004 (gmt 0)

10+ Year Member



Well that would work, If you want to show all 50 guestbook entrys on one page.

That is why i check where to begin and where to end.
And have an maximum amount of entrys that are shown.

But thx for helping :).

baze22

10:11 pm on Dec 29, 2004 (gmt 0)

10+ Year Member



That's what I got for not looking at it closely enough. :) You could add LIMIT $start,$maxrecords to your query and only get the records you want. From the mysql docs:

SELECT * FROM table LIMIT 5,10; # Retrieve rows 6-15

baze

ergophobe

10:13 pm on Dec 29, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



- NO need to loop twice.
- put everything that doesn't need to be reevaluated every loop on the outside of the loop.
- arrasy indexes need to be $var['name'] and not $var[name];

I'm assuming mysql, but it can be modified to any DB easily enough.

$sql = "SELECT * FROM ".$dbprefix."_guestbook";
$result = $db->sql_query($sql);
while ($Guestbook_Record = $db->sql_fetchrow($result) )
{
print "Nr: $i<br />\nNaam: {$Guestbook_Record['name']}<br />\nComment: {$Guestbook_Record['comment']}<br /><br />\n";
}

BlackDex

10:20 pm on Dec 29, 2004 (gmt 0)

10+ Year Member



@Baze22: Ahh thx.. I Thought there whas an option.. But forgot to look for it :).
Thx.. ill go and check that out.

BlackDex

11:15 pm on Dec 29, 2004 (gmt 0)

10+ Year Member



@ergophobe:
Thx for letting me know about the '' i forgot.
Also.. I Think i need the dubble loop(If you refer to the while and for). Becouse the while tells me how many rows there are wich i use to count how much pages i have. And the for loop is used to print that page.

Atleast i think it should be that way.

Any suggestions are welcome :).

baze22

11:44 pm on Dec 29, 2004 (gmt 0)

10+ Year Member



For the number of rows you could always run another query: "SELECT count(*) as num_rows FROM ".$dbprefix."_guestbook WHERE 1";

Guess it's a matter of what you are trying to optimize for memory or speed or code size. Is there a max limit to the number of guestbook entries? I'd proably use the 2 query/limit method to ensure that I'd never have to worry about "how many/what size" guestbook entries there were. When reading them all in, the scrip is going to run slower the more there are. Whereas getting only what you need and querying for the count may run slightly slower on small numbers of records, it should be fairly consistent as the database grows.

baze

ergophobe

2:32 am on Dec 30, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month




And the for loop is used to print that page.

I missed that part, but that makes the code all the less optimized. As Baze22 points out, in that case not only should you not loop twice, you should only retrieve the rows needed for that page.

You need to pass a GET parameter with the current page number (if empty, assume page 1) and use that to determine your limit and offset in your query as suggested by Baze22. Once you have the right query, you plug that in to the code I gave. At that point you're getting pretty efficient.

[edited by: ergophobe at 4:20 pm (utc) on Jan. 13, 2005]

ergophobe

2:40 am on Dec 30, 2004 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Check out this thread as well as the threads referenced within it in msg 2 by Birdman

[webmasterworld.com...]

See also

[webmasterworld.com...]

BlackDex

10:10 am on Dec 30, 2004 (gmt 0)

10+ Year Member



Thx ergophobe, Ill go and take a look at it :).
Unfortinaly i have to go to work now :(.

Ill post my progress.

BlackDex

12:44 pm on Jan 3, 2005 (gmt 0)

10+ Year Member



Well it has been a view days sins my last post.

But i think i have it :).


<?php
global $db, $dbprefix, $Max_Guestbook_Records;

$sql = "SELECT COUNT(*) FROM ".$dbprefix."_guestbook";
$result = $db->sql_query($sql);
$Total_Guestbook_Records = $db->sql_result($result);
echo "Total Records: " . $Total_Guestbook_Records . "<br />\n";

$sql = "SELECT * FROM ".$dbprefix."_guestbook ORDER BY id DESC LIMIT 0, 5"; //0 and 5 will be var's for dynamic change.
$result = $db->sql_query($sql);

print "<div id=\"tGuestbook\">\n";
while ( $Guestbook_Records = $db->sql_fetchrow($result) )
{
$PostTime = date("d-m-Y / H:i:s", $Guestbook_Records['post_time']);
//Pars some special tags.
$Comment = pars_gb_content_second_pass($Guestbook_Records['comment'], $Guestbook_Records['uid']);

print "<div class=\"tGuestbookName\">{$Guestbook_Records['name']}</div>";
print "<div class=\"tGuestbookContent\">$Comment</div>";
print "<div class=\"tGuestbookTime\">$PostTime</div><br />\n\n";
}
print "</div><br /><br />\n";

?>

DaButcher

2:24 pm on Jan 3, 2005 (gmt 0)

10+ Year Member



baze22:

For the number of rows you could always run another query: "SELECT count(*) as num_rows FROM ".$dbprefix."_guestbook WHERE 1";

wont it be faster to do:
SELECT id FROM...;

then use:
mysql_num_rows() on the result?

I've heard several places that the count() gives a greater system load?

[tek-tips.com...]
and other places I've read it..

ps. I might be mistaken here, so dont shoot me :P

baze22

4:17 pm on Jan 3, 2005 (gmt 0)

10+ Year Member




wont it be faster to do:
SELECT id FROM...;

then use:
mysql_num_rows() on the result?

I've heard several places that the count() gives a greater system load?

I hadn't heard that before, but can neither confirm nor deny its accuracy. Here is an opposing viewpoint:

[faqts.com...]

at the php site, a comment for mysql_num_rows mentioned this:

select count(*) may not give correct results if you are using "group by" or "having" in your query, as count(*) is an agregate function and resets eachtime a group-by column changes.

So I guess ultimately it depends on the script it is going into, what you want to optimize for. I still like planning for large databases from the start unless it makes the script prohibitively slow and/or I am absolutely certain of the max size of my database. I don't like the idea of grabbing the entire database just to get a count if I am only going to use 10 at a time. It's better to plan for the future than to have to fix it later. :)

baze

coopster

6:59 pm on Jan 3, 2005 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



That isn't really an opposing viewpoint as the author is suggesting to use an aggregate function ...
SELECT COUNT(*) FROM table
... statement rather than a ...
SELECT * FROM table
... and then running the mysql_num_rows API against the result set. One returns a single row, the other a full-blown result set of the entire contents of the table. Quite a difference indeed.

If you are already returning a result set and merely want to know the number of rows returned while working with that result set I would go with mysql_num_rows. If you are merely running a query just to determine the number or rows, then a COUNT aggregate function is faster than a full on SELECT statement.

baze22

7:42 pm on Jan 3, 2005 (gmt 0)

10+ Year Member



coopster, I agree with you on when to use either of those two methods.

When I said opposing view, I was referring to the overall indications of the 2 articles. The first suggested that count(*) was much slower, the one I listed said it "was much faster".

The comment from the doc section of the php.net site was separate from the article and just showed a situation where count(*) might not give an accurate count, and even if you didn't need the entire result set might have to get it just to get an accurate count.

I was trying to point out that either can be "optimal", that it depends on the application. Looking back at my last post, I wasn't very clear. Guess I need to try that "preview" button before submitting. :)

baze

coopster

8:17 pm on Jan 3, 2005 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Gotcha. Thanks for the clarification. Hey, you got my curiousity up with this statement though...


but can neither confirm nor deny its accuracy

...as I have never tested it myself. So I decided to run a quick test to see how it panned out.

// COUNT(*): 
$sql = "SELECT COUNT(*) FROM zipcodes";
$time_start = microtime(true);
$rows = mysql_query($sql);
$time_end = microtime(true);
$row = mysql_fetch_array($rows);
$num_rows = $row[0];

$time = number_format($time_end - $time_start, 9, '.', ',');
print "\$num_rows = $num_rows --> SELECT COUNT(*) = $time seconds\n";
// mysql_num_rows():
$sql = "SELECT * FROM zipcodes";
$rows = mysql_query($sql);
$time_start = microtime(true);
$num_rows = mysql_num_rows($rows);
$time_end = microtime(true);
$time = number_format($time_end - $time_start, 9, '.', ',');
print "\$num_rows = $num_rows --> mysql_num_rows() = $time seconds\n";
exit;
// Consistently gave me the answer that mysql_num_rows() is faster:
$num_rows = 42738 --> SELECT COUNT(*) = 0.001669884 seconds
$num_rows = 42738 --> mysql_num_rows() = 0.000015974 seconds
You'll notice that I even pushed the bolded code outside of the time capturing for the COUNT(*) option and it still ran slower. To get a truly accurate comparison you would need to move the bolded statements back inside the time capturing statements as you need to fetch the value in order to be comparable.

Just an FYI for those interested.

coopster

8:20 pm on Jan 3, 2005 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



I should clarify myself as well -- the test was to see which would be faster when you already have a result set returned ;)

Note: I also am using PHP5; the microtime() function is a bit different in this version of PHP.

BlackDex

8:57 pm on Jan 3, 2005 (gmt 0)

10+ Year Member



I Tested it also.
And i have the complete opposite.

The SELECT COUNT(*) From table gives me an 0.000684
And the mysql_numrows gives me an 0.003108.

So COUNT(*) seems faster to me.

coopster

12:47 am on Jan 4, 2005 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Interesting. Ready for 20 questions? hehe, just kidding, but I'm interested...

  1. Was that using PHP5 as well?
  2. Which database and which version of the database?
  3. Did you try moving the two bolded statements inside the timing statmements to get a more accurate comparison?
  4. How many rows were you SELECTing?

baze22

1:08 am on Jan 4, 2005 (gmt 0)

10+ Year Member



I've been playing a little with you script and my data - Here's some additional data:

$num_rows = 101875 --> mysql_num_rows() = 0.000019000 seconds
$num_rows = 101875 --> SELECT COUNT(*) = 0.001325000 seconds

Added 255 char column
$num_rows = 101875 --> mysql_num_rows() = 0.000018000 seconds
$num_rows = 101875 --> SELECT COUNT(*) = 0.003385000 seconds

I want to do some more playing with smaller data set and bigger records too. But out of time for today.

baze

BlackDex

5:06 pm on Jan 4, 2005 (gmt 0)

10+ Year Member



'1. Was that using PHP5 as well?'
- I Tryed it with both PHP5 and PHP4 both same result.

'2. Which database and which version of the database?'
- MySQL v4.1.8

'3. Did you try moving the two bolded statements inside the timing statmements to get a more accurate comparison?'
- Yepp, both times it still whas faster.

'4. How many rows were you SELECTing?'
- It only whas 60 rows, but i can make it more :).

I Will try some other stuff.. and post my results.

baze22

5:40 pm on Jan 4, 2005 (gmt 0)

10+ Year Member



I was curious to find out which was quicker if you needed the count prior to getting your result set.

Here's what I got:
$num_rows = 101875 --> SELECT COUNT(*) = 0.000188000 seconds
$num_rows = 101875 --> mysql_num_rows() = 0.022469000 seconds

using this code (rearranged coopster's):


// COUNT(*):
$sql = "SELECT COUNT(*) FROM showdates";
$time_start = microtime(true);
$rows = mysql_query($sql);
$row = mysql_fetch_array($rows);
$num_rows = $row[0];
$time_end = microtime(true);
$time = number_format($time_end - $time_start, 9, '.', ',');
print "\$num_rows = $num_rows --> SELECT COUNT(*) = $time seconds<br>\n";

// mysql_num_rows():
$sql = "SELECT * FROM showdates";
$time_start = microtime(true);
$rows = mysql_query($sql);
$num_rows = mysql_num_rows($rows);
$time_end = microtime(true);
$time = number_format($time_end - $time_start, 9, '.', ',');
print "\$num_rows = $num_rows --> mysql_num_rows() = $time seconds<br>\n";
exit;

baze