So I'm trying to sort from highest prestiges and then highest experience (xp)
public function getRank($user, $skill, $mode) {
$skill2 = strtolower($skill)."_xp";
$skill3 = strtolower($skill)."_prestiges";
$stmt = $this->conn->prepare("SELECT (
SELECT COUNT(*) FROM hs_users WHERE mode = :mode AND (
$skill3 >= u.$skill3 AND $skill2 >= u.$skill2
)
) AS rank
FROM hs_users u
WHERE username = :user AND mode = :mode2
LIMIT 1");
$stmt->bindParam(":user", $user);
$stmt->bindParam(":mode", $mode);
$stmt->bindParam(":mode2", $mode);
$stmt->execute();
return $stmt->fetchColumn();
}
At the moment I'm getting duplicate rankings, two people are rank 1.
For example
if someone is prestige 1 and 3,000,000 experience and then someone is prestige 0 and 4,000,000 experience they achieve the same ranking, as to the second person is suppose to be rank 2.
I've been trying ORDER BY but it doesn't seem to be working. I'm not too experienced with SQL queries, if you have any pointers or could help me that'd be great.
If prestige is the primary ranking attribute:
$stmt = $this->conn->prepare("SELECT id, $skill3, $skill2
FROM hs_users
WHERE username = :user
AND mode = :mode
");
$stmt->bindParam(":user",$user);
$stmt->bindParam(":mode",$mode);
if($stmt->execute()){
$user = $stmt->fetch(PDO::FETCH_ASSOC);// get the users id, prestige and experience level
}
$higherPrestiges = array();
$stmt = $this->conn->prepare("SELECT id
FROM hs_users
WHERE $skill3 >= :skill3
AND mode = :mode");
$stmt->bindParam(":skill3",$user[$skill3]);
$stmt->bindParam(":mode",$mode);
if($stmt->execute()){
while($row = $stmt->fetch(PDO::FETCH_COLUMN)){ //get all users that have higher prestige
$higherPrestiges[$row] = $row;
}
}
if(count($higherPrestiges) > 0){
$higherPrestiges_implode = implode(",",$higherPrestiges);
}else{
$higherPrestiges_implode = "0";
}
$lowerExperiences = array();
$stmt = $this->conn->prepare("SELECT id
FROM hs_users
WHERE $skill3 = :skill3
AND $skill2 < :skill2
AND mode = :mode");
$stmt->bindParam(":skill2",$user[$skill2]);
$stmt->bindParam(":skill3",$user[$skill3]);
$stmt->bindParam(":mode",$mode);
if($stmt->execute()){
while($row = $stmt->fetch(PDO::FETCH_COLUMN)){//get users with lower experience with the same prestige
$lowerExperiences[$row] = $row;
}
}
if(count($lowerExperiences) > 0){
$lowerExperiences_implode = implode(",",$lowerExperiences);
}else{
$lowerExperiences_implode = "0";
}
$stmt = $this->conn->prepare("SELECT COUNT(id)
FROM hs_users
WHERE mode = :mode
AND id IN(".$higherPrestiges_implode.")
AND id NOT IN(".$lowerExperiences_implode.")");
$stmt->bindParam(":mode",$mode);
if($stmt->execute()){
$rank = $stmt->fetch(PDO::FETCH_COLUMN);//get number of users that have higher or equal prestige and exclude the ones with lower experience
}
Just replace references to "id" in the queries with the column name for primary keys in your table
I do realize the last 3 queries can be combined with subqueries in the IN clause, but this should make it easier to understand
Related
How to select one row after update in one query with PDO and PHP?
like this:
function giftUpdate($username,$id,$date,$conn){
$update = $conn->prepare("UPDATE gifts SET used = 1 , user = :user , date = :date WHERE id = :id");
$update->bindParam("id" , $id );
$update->bindParam("date" , $date );
$update->bindParam("user" , $username );
$update->execute();
return $update ? true : false;
}
function giftGet($username,$currnetTime,$conn) {
$statment = $conn->prepare("SELECT * FROM gifts WHERE used = 0 ORDER BY `id` ASC");
$statment->execute();
$gift = $statment->fetch(PDO::FETCH_OBJ);
if($gift){
$voucher = $gift->code;
$voucherid = $gift->id;
$vouchertype = $gift->type;
$voucherkey = $gift->keys;
if($voucher){
$giftUpdate = giftUpdate($username,$voucherid,$currnetTime,$conn);
if($giftUpdate){
useUpdate($username , $conn);
}
}
}
usleep(50000);
return $gift ? $gift : false;
}
After update I need select the same row to send it to user.
(i`am one table for vouchers list with 1000row and column 'used' with 0 by default , and with user request i need in one query update this row to be used 1 and select this row.)
my problem in this functions is Some users will receive a duplicate vouchers And a code is assigned to several users.
guys ... for this case Is it possible to use the transaction method? Or do you suggest using a transaction at all?
Edited:
I eventually changed the code below, but I'm still in line :
SELECT id FROM gifts WHERE used = 0 ORDER BY id ASC limit 1 FOR UPDATE)");
But when I put an id from the database instead of this code, it works!
function giftGet($username, $currnetTime, $conn)
{
$uniqid = uniqid('vouchers_');
$update = $conn->prepare("UPDATE gifts SET used = 1, uniqueid =:uniqueid , user = :user , date = :date WHERE id = (
SELECT id FROM gifts WHERE used = 0 ORDER BY `id` ASC limit 1 FOR UPDATE)");
$update->bindParam("date", $currnetTime);
$update->bindParam("uniqueid", $uniqid);
$update->bindParam("user", $username);
$update->execute();
$updaterowCount = $update->rowCount();
if ($updaterowCount)
{
$statment = $conn->prepare("SELECT * FROM gifts WHERE uniqueid = :uniqueid ORDER BY `id` ASC");
$statment->bindParam("uniqueid", $uniqid);
$statment->execute();
$gift = $statment->fetch(PDO::FETCH_OBJ);
$voucherid = $gift->id;
if ($gift)
{
useUpdate($username, $voucherid, $conn);
}
}
return $gift ? $gift : false;
}
I would alter the table gifts and add a column uniqueid(varchar).
then use
$uniqid = uniqid('vouchers_');
and rewrite your update
"UPDATE gifts SET used = 1, uniqueid =:UNIQUEID , user = :user , date = :date WHERE id = (
SELECT id FROM gifts WHERE used = 0 ORDER BY `id` ASC limit 1 FOR UPDATE)";
now you can identify the updated record by uniqueid.
In Your Code there is a time-gap between the select and the update. 2 or more users can start at the same time the select, recieve the same voucher before it is updated with used = 1
I hope someone can help me with the best way to structure this code.
I run an oil club as a volunteer and if there are no quotes for a specific oil type, then the page fails due to empty variables being used in queries further down the code.
Ideally I would like to default to $win_supplier_red_id = 0 if the query returns no results, but I'm not sure the best way to catch it and where is the best place in the code.
$sql2a= "Select Quote_id from tbl_quote where (select min(quote_price) as best_red from tbl_quote where fuel_type_id =2 AND timestamp > date_sub( NOW(), INTERVAL 7 DAY ) AND quote_price > 10) = quote_price AND timestamp > date_sub( NOW(), INTERVAL 7 DAY ) Order by timestamp Limit 1";
$stmt2a = $db->prepare($sql2a);
$stmt2a->execute();
$res2a = $stmt2a->fetchObject();
$best_red_quote = $res2a->Quote_id;
$sql2= "SELECT qt.quote_id, ft.fuel_type_id, ft.fuel_name, st.supplier_id, st.company_name as company_name, st.email, qt.supplier_id, qt.timestamp, qt.fuel_type_id, min( qt.quote_price ) AS best_red
FROM tbl_quote qt
INNER JOIN `tbl_suppliers` st ON qt.supplier_id = st.supplier_id
INNER JOIN `tbl_fuel-type` ft ON qt.fuel_type_id = ft.fuel_type_id
WHERE qt.Quote_id = $best_red_quote
Order by timestamp";
$stmt2 = $db->prepare($sql2);
$stmt2->execute();
$res2 = $stmt2->fetchObject();
$best_red = $res2->best_red;
$winning_supplier_red = $res2->company_name;
$win_supplier_red_id = $res2->supplier_id;
$stmt2 = $db->prepare($sql2);
$stmt2->execute();
$res2 = $stmt2->fetchObject();
if (is_object($res2)) {
$best_red = $res2->best_red;
$winning_supplier_red = $res2->company_name;
$win_supplier_red_id = isset($res2->supplier_id) ? $res2->supplier_id : 0;
} else {
$best_red = '';
$winning_supplier_red = '';
$win_supplier_red_id = 0;
}
I have a "matrix" table with the following columns filled in.
matrix_id, user_id, position_1, position_2, position_3
1 1 1982 2251 5841
2 2 6204 0 0
3 3 0 0 0
4 4 0 0 0
I basically want to do the following.
Find a row with the lowest user_id and that has an empty position.
In the example above, that would be user_id 2 and position_2.
I update the row with a query.
I then move on to the next next empty position. Since user_id 2 still has an empty position_3, I update the row again with a query.
Since that row is complete, I move on to the next highest user_Id that has empty positions. In this case, it's user_id 3 and then user_id 4 the one after that.
I know I can do all of the above if I know what the user_id is. But assume in this case, I have no clue what the user_ids are. How would the queries look then?
Here is what I have so far.
$find_user = $db->prepare("SELECT * FROM matrix WHERE user_id > :user_id");
$find_user->bindValue(':user_id', 0);
$find_user->execute();
$result_user = $find_user->fetchAll(PDO::FETCH_ASSOC);
if(count($result_user) > 0) {
foreach($result_user as $row) {
$matrix_id = $row['matrix_id'];
$user_id = $row['user_id'];
$position_1 = $row['position_1'];
$position_2 = $row['position_2'];
$position_3 = $row['position_3'];
}
} else {
$errors[] = 'User Id not found in Matrix.';
}
$update_user = $db->prepare("UPDATE matrix SET position_2 = :position_2 WHERE user_id = :user_id");
$update_user->bindValue(':position_2', 1564;
$update_user->bindParam(':user_id', $user_id);
if($update_user->execute()) {}
This should go through all your users from smallest user_id to largest.
For each user it will check the relevant columns in order and apply a new value to the empty ones.
$new_val = 1999;
$result = $db->query("SELECT * FROM matrix ORDER BY user_id");
$users = $result->fetchAll(PDO::FETCH_ASSOC);
if(count($users) > 0) {
// prepare all the possible queries
// make use of prepare once execute many times
$stmt1 = $db->prepare("UPDATE `matrix` SET `position_1` = :pos WHERE `user_id` = :id");
$stmt2 = $db->prepare("UPDATE `matrix` SET `position_2` = :pos WHERE `user_id` = :id");
$stmt3 = $db->prepare("UPDATE `matrix` SET `position_3` = :pos WHERE `user_id` = :id");
foreach($users as $user) {
if ( $user['$position_1'] == 0 ) {
$stmt1->execute( array(':pos'=>++$new_val,':id'=>$user['user_id']) );
}
if ( $user['$position_2'] == 0 ) {
$stmt1->execute( array(':pos'=>++$new_val,':id'=>$user['user_id']) );
}
if ( $user['$position_3'] == 0 ) {
$stmt1->execute( array(':pos'=>++$new_val,':id'=>$user['user_id']) );
}
}
} else {
$errors[] = 'User Id not found in Matrix.';
}
You could reduce the rows to process by changing the query a bit to only find users with columns to fix
$result = $db->query("SELECT *
FROM matrix
WHERE position_1 = 0
OR position_2 = 0
OR position_3 = 0
ORDER BY user_id");
important thing here, is that you are working with the row, not columns
so check all the prerequisites and update the row.
$find_user = $db->prepare("SELECT * FROM matrix order by user_id asc");
$find_user->execute();
$result_user = $find_user->fetchAll(PDO::FETCH_ASSOC);
foreach($result_user as $row) {
$matrix_id = $row['matrix_id'];
$user_id = $row['user_id'];
$position_1 = $row['position_1'];
$position_2 = $row['position_2'];
$position_3 = $row['position_3'];
}
$str = ''
if (!$position_2){
$str = "position_2 = :position_2"
} else if (!$position_2 && !$position_3){
$str = "position_2 = :position_2 and position_3 = :position_3"
}
$update_user = $db->prepare("UPDATE matrix SET " . $str . " WHERE user_id = :user_id");
$update_user->bindValue(':position_2', 1564);
$update_user->bindValue(':position_3', 1564);
$update_user->bindParam(':user_id', $user_id);
if($update_user->execute()) {}
Also, get all the rows in the matrix table ordered by used_id and process every row, depending on your condition.
How can I optimize this function? I need to loop though users, and then loop through user submitted data, and update values, it's taking > 1 minute for 264 users with around 80 records per user.
public function calculateUserPoints(){
set_time_limit(0);
/* Select users */
$sql = "SELECT user_id FROM users";
$query = $this->db->prepare($sql);
$query->execute();
$users = $query->fetchAll(PDO::FETCH_ASSOC);
/* User guesses Query */
$ug_sql = "SELECT
guesses.guess_id AS r_guess_id, guesses.user_id, guesses.game_id, guesses.team_1_score AS guess_team_1, guesses.team_2_score AS guess_team_2, guesses.joker,
games.game_id, games.game_team_1, games.game_team_2, games.real_score_team_1, games.real_score_team_2,
users.user_id AS usr_id
FROM games
JOIN users
ON users.user_id = :user_id AND games.real_score_team_1 IS NOT NULL AND games.real_score_team_2 IS NOT NULL
LEFT JOIN guesses
ON guesses.user_id = users.user_id
AND guesses.game_id = games.game_id
";
$ug_query = $this->db->prepare($ug_sql);
foreach($users as $u){ /* Loop users */
/* Get guesses per user basis */
/*echo 'User '.$u['user_id'].'<br/>';*/
$ug_query->bindParam(':user_id', $u['user_id']);
$ug_query->execute();
$usr_guesses = $ug_query->fetchAll(PDO::FETCH_ASSOC);
$u_points = 0;
$sql = "UPDATE users SET points = :u_points WHERE user_id = :usr_id";
$query = $this->db->prepare($sql);
$sql2 = "UPDATE guesses SET guess_points = :guess_points WHERE guesses.guess_id = :guess_id";
$query2 = $this->db->prepare($sql2);
foreach($usr_guesses AS $ug){
$err = false;
$g_points = 0;
if(isset($ug['guess_team_1']) && isset($ug['guess_team_1'])){
$g_points = $this->calcPoints($ug['guess_team_1'], $ug['guess_team_2'], $ug['real_score_team_1'], $ug['real_score_team_2'], $ug['joker']);
} else {
$u_points -= 1;
}
$u_points += $g_points;
/* echo $ug['guess_team_1'].' - '.$ug['guess_team_2'].' :: '.' '.$ug['real_score_team_1'].' - '.$ug['real_score_team_2'].' jk: '.$ug['joker'].' / pt: '.$g_points.':: T: '.$u_points.'<br/>';*/
$query->bindParam(':u_points', $u_points);
$query->bindParam(':usr_id', $u['user_id']);
$query2->bindParam(':guess_points', $g_points);
$query2->bindParam(':guess_id', $ug['r_guess_id']);
if($query->execute() && $query2->execute()){
$err = false;
} else {
$err = true;
}
}
}
if($err == true){
return false;
} else {
return true;
}
}
guess_id and user_id are indexes on all columns.
I'm not looking for a code answer, I'd prefer merely a push in the right direction.
Thanks.
$sql = "SELECT user_id FROM users"; can be joined with $ug_sql query into one query.
If you use InnoDB tables, you can do the updates in transaction, it will speed it up.
And think about removing some indexes, they slow down inserts, updates, and deletes.
How to select one row with max(select field) and another (specific field)?
Below is my code
1. Get to know max(select field) first
2. Then select all field within the row.
but step 2 doesn't return any thing, wonder know why?
And is there shorter syntax for same result??
Thanks.
$gid = 1;
// get lid
$sth = $db->prepare("SELECT MAX(lid) as lid FROM t WHERE gid = :gid");
$sth->bindParam(':gid', $gid);
$sth->execute();
$arr = $sth->fetch(PDO::FETCH_ASSOC);
print $arr['lid'];
// nothing return??
$lid = $arr['lid'];
$sth = $db->prepare("SELECT * FROM t WHERE gid = :gid AND lid = :lid");
$sth->bindParam(':gid', $gid);
$sth->bindParam(':lid', $lid);
$row = $sth->fetch();
print_r($row);
You could do it in one step with this query:
$sql = "SELECT t.*
FROM t
INNER JOIN (
SELECT MAX(lid) as lid
FROM t
WHERE gid = :gid
) AS x ON t.lid = x.lid";
$sth = $db->prepare($sql);
$sth->bindParam(':gid', $gid);
$sth->execute();
$arr = $sth->fetch(PDO::FETCH_ASSOC);
print_r($arr);