Sorting and Ranking values of columns in mysql - php

Lets say I have 3 columns and 3 rows, the first column is for ID, the second is names, third is votes. like:
+----+------+-------+
| id | name | votes |
+----+------+-------+
| 1 | bob | 7 |
| 2 | jill | 2 |
| 3 | jake | 9 |
+----+------+-------+
How can I have PHP compare the values in the votes field and sort it by whichever had the highest number, and attach a rank of #1,2,3, etc. depending on how many votes it had?
Each value will be displayed on a separate page. For example, if I went to 'bob's page' with the ID of 1, I would need it to display '#2 bob' since he would be ranked 2nd by votes.

You can make a separate column rank and update it by running the following code whenever your vote changes. This method will make you more efficient as in this you wont be sorting the table again and again when user visits his page:
$q = "select * from tableName order by votes DESC";
$a = mysql_query($q);
$count = 1;
while($arr = mysql_fetch_array($a){
$up = "update tableName set(rank) VALUES($count) WHERE name=$arr['name']";
$aq = mysql_query($up);
$count++;
}
Now on individual pages, you can just retrieve the rank value and show
$user = "Bob";
$q = "select rank from tableName where name=$user";
$a = mysql_query($q);
$arr = mysql_fetch_array($a);
echo $arr[0];
Also this(a slight modification in other answer) should work for you :-
SELECT #rownum:=#rownum+1 AS rank, name, vote FROM table, (SELECT #rownum:=0) as P ORDER BY vote DESC

You could read the values returned by your query into an array and then sort said array.

You want a SELECT query doing an ORDER BY votes, and create a special variable to handle the rank of a row:
SELECT #rownum:=#rownum+1 AS rank, name, vote FROM table, (SELECT #rownum:=0) ORDER BY vote DESC
This query should let you fetch an array with the rank, name, and number of votes of each person of your table.
Alternatively, you can just sort by vote, and add the rank value yourself with PHP.

You can use the MySQL 'ORDER BY' and display those ranks using PHP:
For this example:
<?php
//connection
//DB selection
$query = "SELECT * FROM table_votes ORDER BY votes DESC";
$result = mysql_query($query);
for(int $i=1; $row = mysql_fetch_array($result);i++)
{
echo "#".$i.$row['name']."<br/>";
}
?>

Related

Determine page from post permalink in paginated forum

The posts in my forum all have an individual permalink.
The posts are paginated and the users can sort them by date and rating.
When a permalink is accessed, the page the post resides on needs to be calculated due to the pagination.
The posts are stored with an auto incrementing id in a mysql innodb table.
At the moment I use the following for the calculation when the posts are sorted by their score (rating):
<?php
// These variables originate from the corresponding uri segments
// For example: http://domain.tld/topics/[ID]/[SLUG]/[POST_ID]
$post_id = $uri->segment(4);
$topic_id = $uri->segment(2);
$post_per_page = 10;
$query = $db->query('SELECT id FROM topic_posts WHERE topic_id = ' . $topic_id . ' ORDER BY score desc');
foreach ($query as $key => $post)
{
if ($post->id == $post_id)
{
$postOnPage = ceil(($key + 1) / $post_per_page);
break;
}
}
However, the amount of posts will keep increasing and fetching all posts seems awkward.
For the date sorting I use the following query, but it's not working for the rating sorting as the post id's are then not in incrementing order:
SELECT CEIL((COUNT(*) + 1) / $posts_per_page) FROM topic_posts WHERE topic_id = $topic_id AND id < $post_id;
So... How can I avoid the php foreach loop and achieve the same with the db query?
I just had an idea for a completely different approach, so I add it as a second answer instead of editing the first.
For this to work predictably, you always have to include at least one unique column into your sorting as a tie breaker. I assume that many of your posts will actually have the same rating, so maybe you should for example ORDER BY score DESC, id DESC to additionally order posts with the same rating as latest first (I think this might make sense for a forum anyway).
Then for the sort order mentioned above, you can get the number of posts that sort BEFORE the post in question with the following query:
SELECT COUNT(1) FROM topic_posts
WHERE topic_id = $topic_id
AND ((score > $post_score) OR (score = $post_score AND id > $post_id));
This is a query that you can optimize using indizes etc. as needed.
It is possible to generate an artificial "row number" within the query, which could then be used to calculate the page the post would appear on. However, depending on your schema and table size this query might become very costly, so be sure to check the performance.
I think it is best described with a demonstration:
First the table structure and some test data:
mysql> CREATE TABLE foo (a VARCHAR(10));
mysql> INSERT INTO foo VALUES ("foo"), ("bar"), ("baz");
A query that returns all results in some order with their row numbers attached:
mysql> SELECT f.*, #rownum := #rownum+1 AS rank FROM foo f, (SELECT #rownum := 0) r ORDER BY f.a;
+------+------+
| a | rank |
+------+------+
| bar | 1 |
| baz | 2 |
| foo | 3 |
+------+------+
3 rows in set (0.00 sec)
You can then use this to select only a particular row:
mysql> SELECT * FROM (SELECT f.*, #rownum := #rownum+1 AS rank FROM foo f, (SELECT #rownum := 0) r ORDER BY f.a) t WHERE a = "foo";
+------+------+
| a | rank |
+------+------+
| foo | 3 |
+------+------+
1 row in set (0.00 sec)
Essentially, you are wrapping the inner numbering query within an outer query that only selects the wanted result row. As you can see the rank is still the same as in the "all results" case, so can now use this row number to calculate your result page.
If you want to paginate over only a subset of all records in the table (for example all posts in a particular forum), that corresponding WHERE clause goes into the inner SELECT where the ORDER BY is:
mysql> SELECT * FROM (SELECT f.*, #rownum := #rownum+1 AS rank FROM foo f, (SELECT #rownum := 0) r WHERE a != "baz" ORDER BY f.a) t WHERE a = "foo";
+------+------+
| a | rank |
+------+------+
| foo | 2 |
+------+------+
1 row in set (0.00 sec)
The downside is that it actually has to iterate over all records (well, all records that match your inner WHERE clause), so it becomes very slow with large tables.

How do you return rows from Mysql based on highest value in table column?

I have a function that is supposed to search the db for the highest 'score'.
The Db is structured like this:
----------------------------------------
| id | UrlId | Article | Score |
----------------------------------------
I can get the highest score correctly, but I do not know how to return the full object based on the highest score.
I am reluctant to loop through the entire table and test the values of 'score' to see which is the highest (although as I type that I suspect I am doing it anyway) because the db will potentially have 10000's of records.
I am sure that this is dead simple, but I have "the dumb and I cant brain today" Does anyone know a more elegant solution?
My end result would have to be something like this:
if there are 4 UrlId;s with the same top score, the user would need to see:
UrlId example1 20(score)
UrlId example2 20(score)
UrlId example3 20(score)
UrlId example4 20(score)
all other results would not be displayed.
function gethappiestBlog() {
$happiestBlogs = /* This is the data that I loop through, this is correct */
$happinessArray = array();
foreach($happiestBlogs as $happiestBlog) {
$happinessArray[]= $happiestBlog->Score;
}
$maxHappy = max($happinessArray);
echo $maxHappy;
}
SELECT fieldlist
FROM `tableName`
WHERE `score` = (SELECT MAX(`score`) FROM `tableName`)
Couldn't you use a query?
SELECT *
FROM table_name
ORDER BY score
DESC LIMIT 1;
If you need multiple scores, you could then use a subquery:
SELECT *
FROM table_name
WHERE score =
(SELECT score
FROM table_name
ORDER BY score
DESC LIMIT 1;
);
Try this.
$dbh=new PDO(DSN,USERNAME,PASSWORD);
$stmt=$dbh->prepare("SELECT * FROM TABLE_NAME ORDER BY Score DESC");
$stmt->execute();
while($happiestBlog=$stmt->fetch(PDO::FETCH_OBJ)):
echo $happiestBlog->Score;
endwhile;
Here ORDER BY Score DESC fetch the the row first ehich has highest Score.

count in sql DESC order

I have this code for counting number of replies in one topic,
$sql = mysql_query("SELECT count(*) FROM ".prefix."REPLY WHERE TOPIC_ID = '$t' AND R_AUTHOR = '$m' ".$Open_SQL." ") or die(mysql_error());
$Count = mysql_result($sql, 0, "count(*)");
if ($Count > 0) {
$Count = $Count;
}
else {
$Count = "";
}
return($Count);
mysql_free_result($sql);
}
it shows the results fine, what I need is to order this results in DESC.
any suggestions?
You are just selecting Count(*) which means there is nothing to sort, because you will exactly get ONE dataRow, containing the count.
What you probably want to do is to add a GROUP BY-clause and select different Stuff and order it by count:
table
user | type
1 apple
2 apple
3 cherry
With such a table, you can group by type and sort the cherry/apple count later.
SELECT count(*) AS c, type FROM table GROUP BY type ORDER by c DESC/ASC
Result:
c | type
2 apple
1 cherry
But without GROUP BY it would just return the total count: 3 - no sorting possible of course.
you probably want to do something like this
SELECT some-field, count(*) as count
FROM your-table
WHERE your-clauses
AND more-clauses
GROUP BY some-field
ORDER BY some-field desc

select mysql missing columns in php

i need to get the latest order (from our custon admin panel). here's my query:
select *
from order
left join customer
on (customer.id = order.fk_cid)
where date = curdate()
order by time desc
limit 1;
this output everything from orders and customers which i need except 1 therefore that is why i use the *
here's my table structure:
order table:
id, fk_cid, date, time
customer table:
id, name, lastname, street, city, zip, country, phone, email, lastlogin
now, in my php i have:
$result = mysql_query("
select *
from `order`
left join customer
on (customer.id = order.fk_cid)
where date = curdate()
order by time desc
limit 1");
$row = mysql_fetch_assoc($result, MYSQL_ASSOC);
at this point my order is not correct, why?
Your customers.id is overwriting the order.id because you are using the same column name.
select *
from `order`
left join customer on (customer.id = order.fk_cid)
where date = curdate() order by time desc limit 1;
+------+--------+------------+----------+------+-------+------
| id | fk_cid | date | time | id | name | ....
+------+--------+------------+----------+------+-------+------
| 1 | 2 | 2011-11-30 | 07:01:23 | 2 | asasd | ....
+------+--------+------------+----------+------+-------+------
1 row in set (0.03 sec)
As you can see in this example you have two id, so PHP when retrieve the data using mysql_fetch_assoc it overwrites the second id because it's the same key in the array. To fix this, you will have to specify the columns in your query:
select `order`.id AS order_id, customer.id AS customer_id, customer.name /* etc... */
This will output:
Also, I recommend to use different name for your tables and fields. order, date, time since they are reserved word (in case you forget for use the ` ).
Array
(
[order_id] => 1
[customer_id] => 2
// etc...
)
Also here's a topic you should read: Why is SELECT * considered harmful?

Sql results into php array

I would like to create an array (in php) from sql results like this:
We have the sql-table "Posts" which stores the Name and the Message.Example:
Name | Message
John | Hello
Nick | nice day
George | Good bye
John | where
What i want is to output the names of people who have posted a message but dont display the same names more than 1 time.
So the output would be John,Nick,George.
(From these records, we see that John has posted 2 messages, but at the final output, we see only one time his name).
Is this somehow possible?
Thanks in advance.
Try:
$sql = <<<END
SELECT DISTINCT Name FROM Posts
END;
$query = mysql_query($sql) or die($sql . ' - ' . mysql_error());
$names = array();
while ($row = mysql_fetch_array($query)) {
$names[] = $row[0];
}
print_r($names);
SELECT DISTINCT
You could run a SQL query to just select the distinct names, and nothing else:
SELECT DISTINCT Name FROM Posts;
This will give you a result set consisting of distinct Names values, with each unique value only being returned 1 time in the set.
to get the count you will need to aggregate using group by:
SELECT
NAME
, COUNT(*) as Posts
FROM
Posts
GROUP BY
NAME
Here is the SQL if you are not averse to group BY
select count(name) as N, name from posts group by name ;
People having more than 1 post
select count(name) as N, name from posts group by name having N > 1 ;

Categories