Getting a specific value from my database with an operator - php

I'm trying to give my users a rank based on the amount of posts they posted. I made a database containing a rankName row with "beginner, novice, itermediate,... to master" and a minimum row with some numbers. I tried to compare the amount of posts ($qtyPosts) with the minimum rows.
For example: When a user has 9 posts, he gets the rank Novice (which has a minimum of 5 posts).
This is the code i wrote for that.
PHP code
// calculate number of posts from user
$rowsPosts = $user->getQuantityOfPosts($userID);
$qtyPosts = 0;
foreach ($rowsPosts as $q) {
$qtyPosts++;
}
//status
$conn = db::getInstance();
$rank = "";
$statementRank = $conn->prepare("SELECT * FROM rank WHERE rank.minimum >= $qtyPosts");
$statementRank->execute();
while($row = $statementRank->fetch(PDO::FETCH_ASSOC) ){
$rank = $row['rankName'];
}
HTML code
<h3>Status: <?php echo $rank; ?></h3>
However, It doesn't post the right rank, instead it posts the latest one, "master". Anyone any idea?

Consider your WHERE clause:
WHERE rank.minimum >= $qtyPosts
If the user is at the lowest rank, then all ranks will be >= that user's post count.
You can keep the same logic, but simply add an order and limit. Something like this:
WHERE rank.minimum >= $qtyPosts ORDER BY rank.minimum LIMIT 1
This would sort the ranks from lowest to highest and just select the first one.

I guess you should have a maximum column as well in the table and change the query to
SELECT * FROM rank WHERE rank.minimum >= $qtyPosts AND rank.maximum < $qtyPosts
Since according to your current post, you'll get all the ranks with $qtyPosts > the minimun number.

Related

PHP PDO pagination foreach

I'm trying to create a pagination for my PDO query. I cant figure it out. I've tried numerous google searches, but nothing that will work for me. [I probably didn't search hard enough. I'm not sure]
This is my code:
$sql2 = "SELECT * FROM comments WHERE shown = '1'ORDER BY ID DESC";
$stm2 = $dbh->prepare($sql2);
$stm2->execute();
$nodes2= $stm2->fetchAll();
foreach ($nodes2 as $n1) {
echo "text";
}
I want to be able to limit 10 comments per page, and use $_GET['PAGE'] for the page.
Something that I tried
$sql2 = "SELECT * FROM comments WHERE shown = '1'ORDER BY ID DESC";
$stm2 = $dbh->prepare($sql2);
$stm2->execute();
$nodes2= $stm2->fetchAll();
$page_of_pagination = 1;
$chunked = array_chunk($nodes2->get_items(), 10);
foreach ($chunked[$page_of_pagination] as $n1) {
echo "text";
}
If someone could help out, I appreciate it.
You need to limit the query that you are performing, getting all values from the database and then limiting the result to what you want is a bad design choice because it's highly inefficient.
You need to do this:
$page = (int)$_GET['PAGE']; // to prevent injection attacks or other issues
$rowsPerPage = 10;
$startLimit = ($page - 1) * $rowsPerPage; // -1 because you need to start from 0
$sql2 = "SELECT * FROM comments WHERE shown = '1' ORDER BY ID DESC LIMIT {$startLimit}, {$rowsPerPage}";
What LIMIT does:
The LIMIT clause can be used to constrain the number of rows returned by the SELECT statement. LIMIT takes one or two numeric arguments, which must both be nonnegative integer constants
More information here: http://dev.mysql.com/doc/refman/5.7/en/select.html
Then you can proceed getting the result and showing it.
Edit after comment:
To get all the pages for display you need to know how many pages are there so you need to do a count on that SELECT statement using the same filters, meaning:
SELECT COUNT(*) as count FROM comments WHERE shown = '1'
Store this count in a variable. To get the number of pages you divide the count by the number of rows per page you want to display and round up:
$totalNumberOfPages = ceil($count / $rowsPerPage);
and to display them:
foreach(range(1, $totalNumberOfPages) as $pageNumber) {
echo '' . $pageNumber . '';
}

Ranking/numbering in php

I tried to achieve a method in which I create a scoreboard (while loop) and order the fetched results by a certain numeric field (points). But what I need to achieve is like the following
rank----username--point
1st------test-----------3200
2nd-----test2---------1200
etc.. I paginate my results and limit 25 results per page. I tried a while loop in the following way
while($row = mysql_fetch_array($query) || $i<=$totalnumberofrows ){
echo out the results
$i++
}
What this does is number from 1 to 25 perfectly, but on the other hand, on page 2 it numbers again from 1-25.
Is there a trick to achieve what I need to achieve?
I.e. continuous numbering from 1 to (total number of rows) even when paginated.
What you're looking for is the MySQL LIMIT statement. For example, the following query would return entry 0-24 (so, the first 25 entries in your database):
SELECT * FROM entries ORDER BY `points` LIMIT 0, 25
Say that you want to fetch the second page (the next 25 entries), you can do:
SELECT * FROM entries ORDER BY `points` LIMIT 25, 25
Most important thing to note here is that the first argument is the row where it should start, and the second argument is not the last row, but the total amount of rows it should return.
To determine the starting point, simply do (($page_number - 1) * 25), assuming your first page is numbered 1. You could for example do the following:
<?php
$start = (($page_number - 1) * 25);
$query = "SELECT * FROM entries ORDER BY `points` LIMIT {$start}, 25";
// ... rest of your code goes here ...
?>
just add 25 to the number for each page,
for the second page (in my example) - add 25
for the third page - add 50
etc...
$page = 2;
while($row = mysql_fetch_array($query) || $i<=$totalnumberofrows ){
echo out the results
echo (($page-1)*$totalnumberofrows)+$i;
$i++
}
If you know the page number and the offset you should be able to do the following
$rank = $offset * $page_number;

while (mysql_fetch_array) in a while loop

i have this code:
while ($sum<16 || $sum>18){
$totala = 0;
$totalb = 0;
$totalc = 0;
$ranka = mysql_query("SELECT duration FROM table WHERE rank=1 ORDER BY rand() LIMIT 1");
$rankb = mysql_query("SELECT duration FROM table WHERE rank=2 ORDER BY rand() LIMIT 1");
$rankc = mysql_query("SELECT duration FROM table WHERE rank=3 ORDER BY rand() LIMIT 1");
while ($rowa = mysql_fetch_array($ranka)) {
echo $rowa['duration'] . "<br/>";
$totala = $totala + $rowa['duration'];
}
while ($rowb = mysql_fetch_array($rankb)) {
$totalb = $totalb + $rowb['duration'];
}
while ($rowc = mysql_fetch_array($rankc)) {
$totalc = $totalc + $rowc['duration'];
}
$sum=$totala+$totalb+$totalc;
}
echo $sum;
It works fine, But the problem is until "$sum=16" the "echo $rowa['duration']" executes, the question is, is there a away to "echo" only the latest executed code in the "while ($rowa = mysql_fetch_array($ranka))" i this while loop?
Because most of the times returns all the numbers until the "$sum=16"
You are explicitly echoing the $rowa['duration'] in the first inner while loop. If you only want to print the last duration from the $ranka set, simple change the echo to $rowa_duration = $rowa['duration'] then echo it outside the loop.
while ($rowa = mysql_fetch_array($ranka)) {
$rowa_duration = $rowa['duration'];
$totala = $totala + $rowa['duration'];
}
echo $rowa_duration . '<br/>';
What you are doing there is bad on multiple levels. And your english horrid. Well .. practice makes perfect. You could try joining ##php chat room on FreeNode server. That would improve both your english and php skills .. it sure helped me a lot. Anyway ..
The SQL
First of all, to use ORDER BY RAND() is extremely ignorant (at best). As your tables begin the get larger, this operation will make your queries slower. It has n * log2(n) complexity, which means that selecting querying table with 1000 entries will take ~3000 times longer then querying table with 10 entries.
To learn more about it , you should read this blog post, but as for your current queries , the solution would look like:
SELECT duration
FROM table
JOIN (SELECT CEIL(RAND()*(SELECT MAX(id) FROM table)) AS id) as choice
WHERE
table.id >= choice.id
rank = 1
LIMIT 1
This would select random duration from the table.
But since you you are actually selecting data with 3 different ranks ( 1, 2 and 3 ), it would make sense to create a UNION of three queries :
SELECT duration
FROM table
JOIN (SELECT CEIL(RAND()*(SELECT MAX(id) FROM table)) AS id) as choice
WHERE
table.id >= choice.id
rank = 1
LIMIT 1
UNION ALL
SELECT duration
FROM table
JOIN (SELECT CEIL(RAND()*(SELECT MAX(id) FROM table)) AS id) as choice
WHERE
table.id >= choice.id
rank = 2
LIMIT 1
UNION ALL
SELECT duration
FROM table
JOIN (SELECT CEIL(RAND()*(SELECT MAX(id) FROM table)) AS id) as choice
WHERE
table.id >= choice.id
rank = 3
LIMIT 1
Look scary, but it actually will be faster then what you are currently using, and the result will be three entries from duration column.
PHP with SQL
You are still using the old mysql_* functions to access database. This form of API is more then 10 years old and should not be used, when writing new code. The old functions are not maintained (fixed and/or improved ) anymore and even community has begun the process of deprecating said functions.
Instead you should be using either PDO or MySQLi. Which one to use depends on your personal preferences and what is actually available to you. I prefer PDO (because of named parameters and support for other RDBMS), but that's somewhat subjective choice.
Other issue with you php/mysql code is that you seem to pointlessly loop thought items. Your queries have LIMIT 1, which means that there will be only one row. No point in making a loop.
There is potential for endless loop if maximum value for duration is 1. At the start of loop you will have $sum === 15 which fits the first while condition. And at the end that loop you can have $sum === 18 , which satisfies the second loop condition ... and then it is off to the infinity and your SQL server chokes.
And if you are using fractions for duration, then the total value of 3 new results needs to be even smaller. Just over 2. Start with 15.99 , ends with 18.01 (that's additional 2.02 in duration or less the 0.7 per each). Again .. endless loop.
Suggestion
Here is how i would do it:
$pdo = new PDO('mysql:dbname=my_db;host=localhost', 'username', 'password');
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$sum = 0;
while ( $sum < 16 )
{
$query = 'that LARGE query above';
$statement = $pdo->prepare( $query );
if ( $statement->execute() )
{
$data = $statement->fetchAll( PDO::FETCH_ASSOC );
$sum += $data[0]['duration']+$data[1]['duration']+$data[2]['duration'];
}
}
echo $data[0]['duration'];
This should do what your code did .. or at least, what i assume, was your intentions.

How to retrieve first 2 results from mysql array without querying again?

Each page lists all the coupons available for a specific retailer. I query the database for all the coupon codes in the header since I count the number of rows returned and use that info in the meta title of the page. I now also want to display the titles of the first 2 coupons in the array. How would I go about extracting the first 2 results from the array without querying the database again?
This is what I have so far:
$retailer_coupons = "select C.couponid,C.fmtc_couponid,C.merchantid,C.exclusive,C.label,C.shoppingtip,C.restrictions,C.coupon,C.custom_order,C.link,C.image,C.expire,C.unknown,M.name,M.approved,M.homepageurl,M.category from tblCoupons C,tblMerchants M where C.merchantid=M.merchantid and C.begin < ".mktime()." and C.expire > ".mktime()." and C.merchantid=".$merchantid." and M.display='1' and C.user_submitted='' order by C.custom_order desc, C.coupon desc";
$retailer_coupons_result = mysql_query($retailer_coupons) or die(mysql_error());
$count_coupons=mysql_num_rows($retailer_coupons_result);
$meta_title = ''.$name.' Coupon Codes ('.$count_coupons.' coupons available)';
Suppose I have 3 records in my table. If I execute below query, I will get 2 results however the count(*) will give me 3 as output
SELECT count(*) FROM temp.maxID limit 2
In your case it will be
$retailer_coupons =
"select C.couponid,C.fmtc_couponid,C.merchantid,C.exclusive,C.label,C.shoppingtip,C.restrictions,C.coupon,C.custom_order,C.link,C.image,C.expire,C.unknown,M.name,M.approved,M.homepageurl,M.category
from tblCoupons C,tblMerchants M
where C.merchantid=M.merchantid
and C.begin < ".mktime()." and C.expire > ".mktime()."
and C.merchantid=".$merchantid." and M.display='1'
and C.user_submitted=''
order by C.custom_order desc, C.coupon desc
limit 2";
limit 2 will do the magic... Cheers!!!
Good Luck!!!
Something like this:
$res = mysql_fetch_assoc($retailer_coupons_result);
$i = 0;
while ($i < 2){
echo $res[$i]['label']."\n";
$i++;
}

PHP MySQL select random rows

I have a problem selecting 6 random friends
This is the query I've got so far:
$result = num_rows("SELECT * FROM friends WHERE member_id = '".$_SESSION['userid']."'");
if($result >= 6) {
$f_num = 6;
} else {
$f_num = $result;
}
for($i = 1; $i <= $f_num; $i++) {
$q_get_member_friends = mysql_query("SELECT * FROM friends WHERE member_id = '".$_SESSION['userid']."' ORDER BY rand() LIMIT 1");
$r_get_member_friends = mysql_fetch_array($q_get_member_friends);
echo $r_get_member_friends['friend_with'];
}
I want to select 6 random friends if the logged in user has more or equal to 6 friends
Stuck on this for a while now :/
Thanks for any help :)
If you use:
SELECT *
FROM friends
WHERE member_id = '".$_SESSION['userid']."'
ORDER BY rand()
LIMIT 6
If the person only has 3 friends, the query will only show those three - it doesn't mean that the query will always return six rows.
The best way I've found to select any number of random records is with OFFSET in the query.
Let's say you want 6 random records, so I'll borrow from an answer above and count the total number of friends in the database.
$sql = mysql_query("SELECT COUNT(*) AS total FROM friends WHERE member_id='". $_SESSION['userid'] ."'");
$get_count = mysql_fetch_array($sql); // Fetch the results
$numfriends = $get_count['total']; // We've gotten the total number
Now we'll get the 6 random records out of the total above (hopefully it's > 6),
$query = mysql_query("SELECT * FROM friends WHERE member_id='". $_SESSION['userid'] ."' LIMIT 6 OFFSET " . (rand(0, $numFriends));
while ($rows = mysql_fetch_array($query))
{
/// show your $rows here
}
Using OFFSET may not be the best or most efficient, but it's worked for me on large databases without bogging them down.
Never mind, I figured it out :)
Had to use while not for :'D
First select the number of friends that the user has:
"SELECT COUNT(*) as numFriends FROM friends WHERE member_id='".$_SESSION['userid']."'
...put that into a variable, let's call it "$numFriends"
Then:
for($z=0;$z<6;$z++)
{
$randomFriendIndex = rand(1,$numFriends);
//Get the friend at that index
}
change limit 1 to limit 6 on the eighth line.
Instead of SELECT * at the beginning, try SELECT COUNT(*) and use the actual return value instead of num_rows().
Your loop could generate duplicates. I would suggest trying OMG Ponies answer.
There is a whole chapter about random selection in the book SQL Antipatterns.

Categories