Is there an easier way to code this XP to LVL script? - php

The system works but I am sure there is another way of coding this in order to make it easier to access the users level depending on their XP points.
$getxp = mysql_query("SELECT `xp` FROM `members` WHERE `id` = '$logged[id]'");
$xp = mysql_fetch_array($getxp);
$x = $xp['xp'];
echo "$x";
$level = MYSQL_QUERY("SELECT * FROM `levels` WHERE `xp` >= '$x' LIMIT 1");
while ($n = mysql_fetch_array($level)) {
$mylvl = $n[level];
echo "You are a level $mylvl";
}
I have a database table for 'MEMBERS' (ID, USERNAME, PASS, XP) and a table for 'LEVELS' (ID, XP, LEVELS). Let me know of an easier method to get the LVL for the user. Many Thanks!

I think this should do it.
SELECT `levels`
FROM `members` m
LEFT JOIN `levels` l ON m.`xp` >= l.`xp`
WHERE m.`id` = '. $logged[id] .'
ORDER BY `levels` ASC
LIMIT 1;
Get all members where your member.id = $logged[id]. Next, link your member.id to all xp-requirements that are smaller than your current exp. This will give you a list of all the levels that you surpassed. Now the only thing you have to do is order them in a way that the highest level is first, and then simply limit it to 1 entry. This should result in the highest level that your exp allows you to have -- therefore your current level.
Note that of course, the more levels you have, the larger the LEFT JOIN becomes, which will end up consuming more memory on your SQL server.
also note you're using MYSQL. This is outdate and will be taken out of the next PHP version. Look into MYSQLi at least, or go straight for PDO. This way you wont have to change all your code once the new PHP releases.

$sql = "SELECT * FROM levels WHERE xp >= (SELECT xp FROM members WHERE id='".$loggedin['id']."')";
subselect not a join as you have no keys relating levels to members

Related

PHP Calculate rank from database

I got a little problem, I've got a database, in that database are different names, id, and coins. I want to show people their rank, so your rank has to be 1 if you have the most coins, and 78172 as example when your number 78172 with coins.
I know I can do something like this:
SELECT `naam` , `coins`
FROM `gebruikers`
ORDER BY `coins` DESC
But how can I get the rank you are, in PHP :S ?
You can use a loop and a counter. The first row from MySql is going the first rank,I.e first in the list.
I presume you want something like:
1st - John Doe
2nd - Jane Doe
..
..
right?
See: http://www.if-not-true-then-false.com/2010/php-1st-2nd-3rd-4th-5th-6th-php-add-ordinal-number-suffix
Helped me a while ago.
You could use a new varariable
$i = "1";
pe care o poti folosi in structura ta foreach,while,for,repeat si o incrementezi mereu.
and you use it in structures like foreach,while,for,repeat and increment it
$i++;
this is the simplest way
No code samples above... so here it is in PHP
// Your SQL query above, with limits, in this case it starts from the 11th ranking (0 is the starting index) up to the 20th
$start = 10; // 0-based index
$page_size = 10;
$stmt = $pdo->query("SELECT `naam` , `coins` FROM `gebruikers` ORDER BY `coins` DESC LIMIT {$start}, {$page_size}");
$data = $stmt->fetchAll();
// In your template or whatever you use to output
foreach ($data as $rank => $row) {
// array index is 0-based, so add 1 and where you wanted to started to get rank
echo ($rank + 1 + $start) . ": {$row['naam']}<br />";
}
Note: I'm too lazy to put in a prepared statement, but please look it up and use prepared statements.
If you have a session table, you would pull the records from that, then use those values to get the coin values, and sort descending.
If we assume your Session table is sessions(session_id int not null auto_increment, user_id int not null, session_time,...) and we assume that only users who are logged in would have a session value, then your SQL would look something like this: (Note:I am assuming that you also have a user_id column on your gebruikers table)
SELECT g.*
FROM gebruikers as g, sessions as s WHERE s.user_id = g.user_id
ORDER BY g.coins DESC
You would then use a row iterator to loop through the results and display "1", "2", "3", etc. The short version of which would look like
//Connect to database using whatever method you like, I will assume mysql_connect()
$sql = "SELECT g.* FROM gebruikers as g, sessions as s WHERE s.user_id = g.user_id ORDER BY g.coins DESC";
$result = mysql_query($sql,$con); //Where $con is your mysql_connect() variable;
$i = 0;
while($row = mysql_fetch_assoc($result,$con)){
$row['rank'] = $i;
$i++;
//Whatever else you need to do;
}
EDIT
In messing around with a SQLFiddle found at http://sqlfiddle.com/#!2/8faa9/6
I came accross something that works there; I don't know if it will work when given in php, but I figured I would show it to you either way
SET #rank = 0; SELECT *,(#rank := #rank+1) as rank FROM something order by coins DESC
EDIT 2
This works in a php query from a file.
SELECT #rank:=#rank as rank,
g.*
FROM
(SELECT #rank:=0) as z,
gebruikers as g
ORDER BY coins DESC
If you want to get the rank of one specific user, you can do that in mysql directly by counting the number of users that have more coins that the user you want to rank:
SELECT COUNT(*)
FROM `gebruikers`
WHERE `coins` > (SELECT `coins` FROM `gebruikers` WHERE `naam` = :some_name)
(assuming a search by name)
Now the rank will be the count returned + 1.
Or you do SELECT COUNT(*) + 1 in mysql...

mysql query select depending on 2 tabels

I've reached the php code that I want. But my website is too slow because it keeps repeating and round over and over again,
$pages= mysql_query("SELECT * FROM ex_instagram_p WHERE type = '".follow."' AND active=1 And username !='".$username."' ORDER BY cpc DESC" );
$prow = mysql_fetch_array($pages);
Do{
$dollowed = mysql_query("SELECT * FROM exchanges WHERE user = '".$username."' AND exid = '".$prow['id']."'");
$followed = mysql_num_rows($dollowed);
;}while($followed > 0 && $prow = mysql_fetch_array($pages));
Actually I can explain the code a little more,
I need to choose the maximum CPC row from the first table
But also to make sure that:
type = '".follow."' AND active=1 And username !='".$username."' ORDER BY cpc DESC"
And here comes the problem,
before continue I need to make sure that the 'id' field form the first table does't got a record on the second table with the user username,
is it got result it'll repeat again using the next row,
over and over until finding a result that satisfies both tables.
This method is soooo heavy
I hope to get a simpler and lighter way, thanks
I've tried to do this but it does not work
$prow= mysql_query("SELECT *
FROM (mysql_query("SELECT * FROM ex_instagram_p WHERE type = '".follow."' AND active=1
And username !='".$username."' ORDER BY cpc DESC" );)
INNER JOIN (mysql_query("SELECT * FROM exchanges WHERE user = '".$username."'");)
ON ex_instagram_p.id=exchanges.exid";);
You can do this in one query. It is a bit hard to follow the logic, but I think this is the query that you want:
SELECT i.*
FROM ex_instagram_p i join
exchanges e
on i.id = e.exid
WHERE i.type = '".follow."' AND i.active=1 And
i.username <> '".$username."' and e.user = '".$username."'
ORDER BY i.cpc DESC
LIMIT 1;
Doing the work in the database is generally much faster than cycling through rows in the application. After all, that is what databases are good for -- managing and querying large amounts of data.

Best way to sum and seperate by date in MYSQL with/witout php

Hi i have such table information:
what i want to do with php with while or just in mysql, is to SUM (time_used) of the rows with status 44 until its reached row with status 55. after that it should begin from start with new summing.
first query should return 37, second 76 (keep in mind it should be universal, for unlimited occurrences of 55 status row)
i thought of a way with time/date filtering and have this:
select sum(time_used) as sumed
from timelog
where start_time > (select end_time from timelog where (status='55')
ORDER BY id DESC LIMIT 1) ORDER BY id DESC
but this works only for last combination of 44 and 55
i know i will need two way filtering( < end_time and > end_time) so it will work for all cases, but cant think of a way to do it in php
can anyone help me?
EDIT:
sqlfiddle whoever want it:
http://sqlfiddle.com/#!2/33820/2/0
There are two ways to do it: Plain SQL or PHP. If you treat thousands of rows, it may be interresting to choose between the two by testing performance.
Plain SQL
select project_id, task_id, user_id, sum(time_used) as time_used,
min(start_time) as start_time, max(end_time) as end_time, max(comment) as comment from
(select t.id, t.project_id, t.task_id, t.user_id, t.time_used,
count(t2.id) as count55, t.start_time, t.end_time, t.comment
from timelog t
left join timelog t2 on t.id>t2.id and t2.status=55 and t.task_id=t2.task_id
group by t.id) as t
group by count55;
I assume here that a task can belong to one user only
SQL and PHP
$link = mysqli_connect( ... );
$query = "select id, project_id, task_id, user_id, time_used, start_time, end_time, status
from timelog order by id";
$result = mysqli_query($link, $query);
$table = array();
$time_used = 0;
$start_sum = true;
$i = 0;
while($row = mysqli_fetch_assoc ($result)){
if($start_sum){
$table[$i] = $row;
$start_sum = false;
} else {
$table[$i]['time_used'] += $row['time_used'];
$table[$i]['end_time'] += $row['end_time'];
}
if($row['state'] == 55){
$i++;
$start_sum = true;
}
}
If two tasks can run in simultaneously, solution 1 will work, but solution 2 will need to be adapted in order to take this in account.
here is my intepretation:
http://sqlfiddle.com/#!2/33820/45
set #n=0;
select project_id, task_id, user_id,sum(time_used) from (
SELECT time_used,project_id, task_id, user_id,
#n:=if(status=55,#n+1,#n),
if(status=55,-1,#n) as grouper FROM timelog
) as t
where grouper>-1
group by grouper;
I'm neither a php nor MySQL programmer, but I can explain the logic you want to follow. You can then code it.
First, query your db and return the results to php.
Next, set two sum variables to 0.
Start looping through your query results. Increment the first sum variable until you reach the first row that has status 55. Once you do, start incrementing the second variable.
The tricky part will be to sort your query by the row number of the table. Here is a link that will help you with that part.

Paginate while ignoring sub rows, with a Right Join

I have this basic query that returns a main row of a program title(eventdesc) and then lists program participants and info as multiple sub rows.
PROGRAM 1
Email 1, email 2, name, status (first participant)
Email 1, email 2, name, status (second participant)
PROGRAM 2
Email 1, Email 2, name, status (first participant)
ETC...
Basic query:
$query_perpage = "SELECT COUNT(*) FROM camps_events";
$result = mysql_query($query_perpage, $db) or die(mysql_error());
$r = mysql_fetch_row($result);
$numrows = $r[0];
$pages = new Paginator($query);
$pages->items_total = $numrows;
$pages->paginate();
$pages->mid_range = 7;
$query = mysql_query("SELECT ce.eventcode, ce.eventdesc, ce.date, r.participant_fname, r.participant_lname,
r.position, r.dob, r.status, r.email_primary, r.order_number
FROM camps_events ce
RIGHT JOIN registrations r on r.eventcode = ce.eventcode
WHERE ce.reg_status = 'Active'
AND r.status NOT IN ('Incomplete','Canceled')".getAllowedPrograms()."
ORDER BY ce.eventdesc, ce.eventcode, r.participant_lname ASC $pages->limit ") or die(mysql_error());
I've been trying to figure out how to paginate it by Program, no matter how many subrows.
I have a pagination class in place on another page, but that only pulls from one table.
I've been trying to implement the same pagination, but I can only get it to pull the first program, and then it limits the page based on the sub rows, not the main program row. And in the state I've posted it here, each following page displays only the first programs. The sub rows that come up are different on each page, but I can't tell if they are all in the first program.
The query works fine for pulling all results properly, but if it's a huge list, it's extremely inefficient, and can even timeout the browser.
Any help would be much appreciated, please let me know if I can include anything else.
I'm not too concerned with using the same pagination class, so I'm completely open to different approaches.
Thanks in advance
Try using a subselect:
SELECT ce.eventcode, ce.eventdesc, ce.date, r.participant_fname, r.participant_lname,
r.position, r.dob, r.status, r.email_primary, r.order_number
FROM camps_events ce
RIGHT JOIN registrations r on r.eventcode = ce.eventcode
WHERE ce.reg_status = 'Active'
AND r.status NOT IN ('Incomplete','Canceled')".getAllowedPrograms()."
AND ce.id in (select id from camps_events pgce order by ce.eventdesc, ce.eventcode $pages->limit)
ORDER BY ce.eventdesc, ce.eventcode, r.participant_lname ASC ")
or die(mysql_error());
Edit: unfortunately MySQL can not yet limit subqueries. :-(
So it has to be a temp-table:
mysql_query("drop temporary table if exists tmp_events", $db);
mysql_query("create temporary table tmp_events select id from camps_events order by eventdesc, eventcode " . $pages->limit, $db);
mysql_query("SELECT ce.eventcode, ce.eventdesc, ce.date, r.participant_fname, r.participant_lname,
r.position, r.dob, r.status, r.email_primary, r.order_number
FROM camps_events ce
inner join tmp_events pgce on pgce.id = ce.id
RIGHT JOIN registrations r on r.eventcode = ce.eventcode
WHERE ce.reg_status = 'Active'
AND r.status NOT IN ('Incomplete','Canceled')".getAllowedPrograms()."
ORDER BY ce.eventdesc, ce.eventcode, r.participant_lname ASC ")
or die(mysql_error());
There can be of course pages with less than paged items, because events without registrations will not show.

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.

Categories