PHP Pagination using count and possibly group by - php

I am using COUNT and GROUP BY to only show unique results in a database query. EX:
$result = $db->query("SELECT *,COUNT(clientname)
FROM recruitingForm
GROUP BY clientname LIMIT $offset, $rowsperpage");
There is also the beginnings of pagination going on, but it is giving me the results of the entire database and not my smaller list.
How do i set up my db query in order to do that?

Fixed - By removing COUNT(clientname). LIMIT was never in my pagination code to begin with.

Related

I need to select newest rows from a MySQL database, but verify that I am also returning a row with a given ID

I'm new to this, sorry if the title is confusing. I am building a simple php/mysql gallery of sorts. It will show the newest 25 entries when a user first goes to it, and also allows off-site linking to individual items in the list. If the URL contains an ID, javascript will scroll to it. But if there are 25+ entries, it's possible that my query will fetch the newest results, but omit an older entry that happens to be in the URL as an ID.
That means I need to do something like this...
SELECT * FROM `submissions` WHERE uid='$sid'
But after that has successfully found the submission with the special ID, also do
SELECT * FROM `submissions` ORDER BY `id` DESC LIMIT 0, 25`
So that I can populate the rest of the gallery.
I could query that database twice, but I am assuming there's some nifty way to avoid that. MySQL is also ordering everything (based on newest, views, and other vars) and using two queries would break that.
You could limit across a UNION like this:
(SELECT * FROM submissions WHERE uid = '$uid')
UNION
(SELECT * FROM submissions WHERE uid <> '$uid' ORDER BY `id` LIMIT 25)
LIMIT 25
Note LIMIT is listed twice as in the case that the first query returns a result, we would have 26 results in the union set. This will also place the "searched for" item first in the returned sort result set (with the other 24 results displayed in sort order). If this is not desirable, you could place an ORDER BY across the union, but your searched for result would be truncated if it happened to be the 26th record.
If you need 25 rows with all of them being sorted, my guess is that you would need to do the two query approach (limiting second query to either 24 or 25 records depending on whether the first query matched), and then simply insert the uid-matched result into the sorted records in the appropriate place before display.
I think the better solution is:
SELECT *
FROM `submissions`
order by (case when usid = $sid then 0 else 1 end),
id desc
limit 25
I don't think the union is guaranteed to return results in the order of the union (there is no guarantee in the standard or in other databases).

php paging and the use of limit clause

Imagine you got a 1m record table and you want to limit the search results down to say 10,000 and not more than that. So what do I use for that? Well, the answer is use the limit clause.
example
select recid from mytable order by recid asc limit 10000
This is going to give me the last 10,000 records entered into this table.
So far no paging.
But the limit phrase is already in use.
That brings to question to the next level.
What if I want to page thru this record particular record set 100 recs at a time? Since the limit phrase is already part of the original query, how do I use it again, this time to take care of the paging?
If the org. query did not have a limit clause to begin with, I'd adjust it as limit 0,100 and then adjusting it as limit 100,100 and then limit 200,100 and so on while the paging takes it course. But at this time, I cannot.
You almost think you'd want to use two limit phrases one after the other - which is not not gonna work.
limit 10000 limit 0,100 for sure it would error out.
So what's the solution in this particular case?
[EDIT]
After I post this question, I realized that the limit 10000 in the org. query becomes meaningless once the paging routine kicks in. I somehow confused myself and though that order by recid asc limit 10000 in its entirety is part of the where clause. In reality, the limit 10000 portion has no bearing in the recordset content - other than taking care of the confining the recordset to the requested limit. So, there is no point of keeping the limit 10000 once the paging starts. I'm sorry for wasting your time with this. :(
I'd say get rid of the first limit, then don't bother doing a count of the table, or take the lesser of the count and your limit, i.e. 10000, and do the pagination based on that.
i.e.
$perpage = 100;
$pages = $totalcount/$perpage;
$page = $get['Page'];
if($page > $pages || $page < 0)
{
$page = 0;
}
$limit = "LIMIT " . ($page * $perpage) . ", " . $perpage;
To calculate totalcount, do
SELECT COUNT(*) FROM mytable
then check it against your limit, i.e.
if($totalcount > 10000)
{
$totalcount = 10000;
}
The reason to do a dedicated count query is that it requires very little DB to PHP data transfer, and many DBMS's can optimize the crap out of it compared to a full table SELECT query.
LIMIT can have two arguments, the first being the offset and the second being how many records to return. So
LIMIT 5,10
Will skip the first 5 records then fetch the next 10.
You will have to set your limit based on the current page. Something like
LIMIT (CURRENT_PAGE -1) * PAGE_SIZE , PAGE_SIZE
So if you had ten records per page and were on page 2 you would skip the first ten records and grab the next ten.
The offset suggestion is a great one and you should probably use that. But if for some reason offset doesn't fit your needs (say someone inserting a new record would shift your page slightly) you could also add a where recid > #### clause to your query. This is how you would paginate when working with Twitter API.
Here is an example in PHP.
<?php
$query = 'select recid from mytable';
if(isset($_GET['recid'])&&$_GET['recid']!=''){
$query = $query.' where recid > '.$_GET['recid'];
}
$query = $query.' order by recid asc limit 10000';
//LOG INTO MYSQL
$result = mysql_query($query);
$last_id = '';
while ($row = mysql_fetch_assoc($result)) {
//DO YOUR DISPLAY WORK
$last_id = $row['recid'];
}
echo '<a href="?recid='.$last_id.'>Next Page</a>';
?>
Again, a bit more complicated than needs to be but will return set pages.
You can use the offset variable. If this is your full query then You could use:
select recid from mytable order by recid asc limit 100 offset 300
for example would give you from 300-399. And obviously you will increase the offset by 100 for every page. So for the first page offset =0, for the second page offset = 100, etc.
to be general offset = (page-1)*100
And as #Mathieu Lmbert said you can make sure the offset doesn't reach 9900.
Effectively there can be only one limit, and it must be in the query. So the only solution will be to adjust the LIMIT clause in your query.
The thing you shouldn't do is to read all 10.000 entries, throwing away the 9900 that you do not want.

How to calculate the total iterations of a while loop when using PHP and MySQL?

The Default code used to LIMIT the results fetched from the MySQL database using PHP is the following,
$sql="SELECT *
FROM `tablename`
WHERE `Type` LIKE '$var1'
LIMIT 0 , 30";
When we need to display results continuously we can alter that code with the following
$start=0; $end=30;
$start=$_GET['start'];
$end=$start+30;
$sql="SELECT *
FROM `tablename`
WHERE `Type` LIKE '$var1'
LIMIT $start , $end";
So I pass the value for the variable start with a link saying Next like this
Next
Everything goes fine when there are more results to display.
Consider this situation: There are 120 Entries in the Database and the PHP file is currently displaying results 91 to 120. So when I click the Next Link now it shows blank. So how can I make the "Next" link to be disabled when it is showing the last set of results? I think that could be possible if we know the total number of entries in the database. But if it is dynamic, how can we calculate it?
If you use a COUNT() function on the result, you can get simple info back.
For example:
$total=mysql_fetch_assoc("SELECT COUNT(*) AS `total` FROM `tablename` WHERE `Type` LIKE '$var1'");
The value returned is the total number of entries that fit the given criteria. So:
$total['total']==120;
You can then simply do something along the lines of:
if (($start+30)>=$total['total']) {
//Greyed out link
}
else {
//Active link
}
It's probably also worth mentioning that your SQL conditions are faulty. The way you have it set up, passing a $start value of '10' to the file would create the query:
$sql="SELECT *
FROM `tablename`
WHERE `Type` LIKE '$var1'
LIMIT 10 , 40";
Which grabs 40 results, starting from result 10, not the results between 10 and 40; a common misconception.
While I'm presuming that's a cut down version of the code, it's always worth using *mysql_real_escape_string()* on anything from the outside that goes into your query. People could easily put the $start value as "; DROP TABLE tablename;", making your query:
$sql="SELECT *
FROM `tablename`
WHERE `Type` LIKE '$var1'
LIMIT ; DROP TABLE `tablename`;";
Which I'm sure you don't want :P
You can use SQL_CALC_FOUND_ROWS as this doesn't take into account the LIMIT clause. You can then use SELECT FOUND_ROWS() to retrieve that value. That way you always get the count at the same time as your original query and avoid any concurrency issues.
Another way to do it would be to always try and get the next result. So always try and retrieve 31 rows, even though you are only displaying 30. You can then check this 31st row and if it exists you know there is another page to display.

How do I query select all my mysql rows under the modification of a stored function with a reddit-based algorithm?

I've spent the whole day googling and deleting and inserting trying to implement this code. I've been trying to implement a reddit-like site using php and mysql. I have been following another question: PHP MYSQL Query Algorithm Help and it works very well and ranks rows according to the algorithm coded in the previous question within myphpadmin when I query a stored function
SELECT
*,
reddit_rank(`time_added`, `up_votes`, `down_votes`) as rank
FROM
`table`
ORDER BY
rank;
, but when I paste the query into my php file:
<?php
include("config.php");
$q= "SELECT *,reddit_rank(`time` , `votes_up` , `votes_down`) FROM `wallposts` ORDER BY rank LIMIT 0 , 30";
$r = mysql_query($q);
if(mysql_num_rows($r) > 0) {
while($row = mysql_fetch_assoc($r)){
...?>
It doesn't work and I get a white HTML screen. So for example in my PHP when I have
$q = "SELECT * FROM wallposts ORDER BY votes_up DESC";
my reddit/facebook-like wall has prepended each of my rows from mysql and everything works just fine. but when i change it to
$q= "SELECT *,reddit_rank(`time` , `votes_up` , `votes_down`) FROM `wallposts` ORDER BY rank LIMIT 0 , 30";
the webpage returns nothing but a white screen even though I know it works in myphpadmin.
Is there something wrong with my syntax or is it not possible to query a select all with a stored function to order the results in php?
I think I found a solution by creating a view and then querying that view instead of the original table. After I queried the stored function in myphpadmin:
SELECT
*,
reddit_rank(`time_added`, `up_votes`, `down_votes`) as rank
FROM
`table`
ORDER BY
rank;
I then created a view after those results were returned. Then instead of querying the same way in my PHP file, I queried the new mysql view with:
SELECT * FROM [view] ORDER BY rank
Voila!

Possible to limit results returned, yet leave 'pagination' to table?

I am building a php site using jquery and the DataTables plugin. My page is laid out just as it needs to be with pagination working but in dealing with large datasets, I have noticed the server is pulling ALL returned rows as opposed to the 10 row (can be more) limit stated within each 'page'.
Is it possible to limit the results of a query and yet keep say the ID numbers of those results in memory so when page 2 is hit (or the result number is changed) only new data is sought after?
Does it even make sense to do it this way?
I just dont want to query a DB with 2000 rows returned then have a 'front-end-plugin' make it look like the other results are hidden when they are truthfully on the page from the start.
The LIMIT clause in SQL has two parts -- the limit and the offset.
To get the first 10 rows:
SELECT ... LIMIT 0,10
To get the next 10 rows:
SELECT ... LIMIT 10,10
To get the next 10 rows:
SELECT ... LIMIT 20,10
As long as you ORDER the result set the same each time, you absolutely don't have to (and don't want to) first ask the database to send you all 2000 rows.
To display paging in conjunction with this, you still need to know how many total rows match your query. There are two ways to handle that --
1) Ask for a row count with a separate query
SELECT COUNT(*) FROM table WHERE ...
2) Use the SQL_CALC_FOUND_ROWS hint in your query, which will tell MySQL to calculate how many rows match the query before returning only the 10 you asked for. You then issue a SELECT FOUND_ROWS() query to get that result.
SELECT SQL_CALC_FOUND_ROWS column1, column2 ... LIMIT 0,10
2 is preferable since it does not add an extra query to each page load.

Categories