How to avoid exponential slowdown in PHP/MYSQL? - php

I'm the owner of an online browser based game that has around 300 players signed up. I've written a script to detect cheaters, but the issue is that the number of queries in said script will grow exponentially.
It works like this:
Send a query that gets player's information.
Inside of the query, run another query that gets the information of every player.
So basically I am running a query that gets every player's name and information, and inside of that query I run another query to get the information from every other player besides themself. I use this to compare and delete cheaters.
The issue is, since I have 300 players, I have to run 300 queries per player. That's 90,000 queries. If I reach 1,000 players, it would be 1,000,000 queries. There has to be a better way to do this.
My code:
<?php
require '../connect.php';
$rulerinfo = $conn->query("SELECT id, rulername, nationname, alliance, email, dateregister, user_agent, lastseen, password FROM players");
while ($rulerinfo2 = $rulerinfo->fetch_assoc()) {
$id = $rulerinfo2['id'];
$rulername = $rulerinfo2['rulername'];
$nationname = $rulerinfo2['nationname'];
$alliance = $rulerinfo2['alliance'];
$email = $rulerinfo2['email'];
$dateregister = $rulerinfo2['dateregister'];
$useragent = $rulerinfo2['user_agent'];
$lastseen = $rulerinfo2['lastseen'];
$password = $rulerinfo2['password'];
$playerinfo = $conn->query("SELECT id, rulername, nationname, alliance, email, dateregister, user_agent, lastseen, password FROM players WHERE id != '$id'");
while ($playerinfo2 = $playerinfo->fetch_assoc()) {
$id2 = $playerinfo2['id'];
$rulername2 = $playerinfo2['rulername'];
$nationname2 = $playerinfo2['nationname'];
$alliance2 = $playerinfo2['alliance'];
$email2 = $playerinfo2['email'];
$dateregister2 = $playerinfo2['dateregister'];
$useragent2 = $playerinfo2['user_agent'];
$lastseen2 = $playerinfo2['lastseen'];
$password2 = $playerinfo2['password'];
$rulerdistance = levenshtein($rulername, $rulername2);
$nationdistance = levenshtein($nationname, $nationname2);
$emaildistance = levenshtein($email, $email2);
$agentdistance = levenshtein($useragent, $useragent2) / 2;
$totaldistance = $rulerdistance + $nationdistance + $emaildistance + $agentdistance;
if ($password == $password2) {
$totaldistance = $totaldistance - 20;
}
if ($totaldistance < 0) {
$totaldistance = 0;
}
}
}
?>

You should only do the query once, put it in an array and work with it from there. I don't see the need to make almost the same query twice. Loop in your array a second time and just check if the id is not the same as the current.
$res = $conn->query("SELECT id, rulername, nationname, alliance, email, dateregister, user_agent, lastseen, password FROM players");
$array=array();
while ($row = $res->fetch_assoc()) {
$array[] = $row;
}
for($i=0; $i<count($array);$i++) {
for($j=0; $j<count($array); $j++) {
if ($i != $j) {
// Call your functions
$rulerdistance = levenshtein($array[$i]['rulername'], $array[$j]['rulername']);
...
}
}
}

Related

Prevent text from being interpreted as a number

I have a PHP/SQL app that processes invoices. Recently, I had an invoice number come in that is not being processed as text, rather as a large exponential number when I do an insert/update on associated SQL tables. For example, take an invoice number that looks like this: 123E456. PHP will try to convert this to an extremely large number due to the 'E' being bookended by numbers.
I am leaning towards this being a PHP issue because when I look at the SQL being sent to the server, it is being scripted without quotes, 123E456 rather than '123E456'.
I have tried multiple ways to try and force it to be text, but nothing seems to work.
If I put single quotes around the string, I get double single quotes in the SQL.
strval() also does not work
the issue might be in the SQL interpreter, but not entirely sure
Right now, I am instructing my clerks to put a space between the E and the numbers, which works for now. But, I am hoping to address this specific issue in the code rather than have the clerk remember to manage it on their end.
Can anyone help with how to force this as being text in the SQL clause?
OK, the code is rather my own style and is based on retrieving a dummy record (the table has 178 columns) and then populating the values into the elements that need updated. It then creates the SQL from the array and does the update. Most of this is just pre-processing to get the values needed. The database being used is Oracle.
function processF0411Z1($id, $user){
include_once $_SERVER['DOCUMENT_ROOT'].'/truck/inc/base.inc.php';
$b = '\' \'';
$z = 0;
$co = get_route_company($id);
$usrsql='SELECT `userID` from `user` where `id` = ' . $user;
$usr = openRecordset_Fetch_Assoc($usrsql);
if($usr[0]==1)$userid = $usr[1]['userID'];
else $userid = $_SESSION['username'];
$jul = date2jul(getdate());
$tjul= getJulTime(getdate());
$sql = "SELECT a.`id`, a.`carrierInvoice`, a.`carrierNbr`, a.`ivd`, a.`dgl`, b.`bol`, b.`obj_acct`, b.`allocation` FROM `route13` a inner join `route131` b on(a.`id` = b.`id`)WHERE a.`id`=".$id;
$myArr = openRecordset_Fetch_Assoc($sql);
if(isset($myArr) && $myArr[0]>0){
$carr = $myArr[1]['carrierNbr'];
$carrsql = 'select `CarrierName` from `Carriers` where `CarrierNbr` = '. $carr;
$carr_res = openRecordset_Fetch_Assoc($carrsql);
if($carr_res[0]==1)$carrName = $carr_res[1]['CarrierName'];
else $carrName = $carr;
// get the next number in the EDI Batch sequence
$nn = getJDEZFileNN();
// get the base associated array of the F0411Z1 table
$msSQL = 'SELECT * FROM PRODDTA.F59411Z1 WHERE VLEDUS=\'TRUCK\' AND VLEDBT=1';
$F0411Z1 = oracle_fetch_array($msSQL);
for($i=1;$i<=$myArr[0];$i++){
// test to see if this record exists
$tsql = "select * from PRODDTA.F0411Z1 where VLEDUS = '".strtoupper($user)."' and VLEDBT = ".$nn[1]['NNN006']." and VLEDLN = " .$i*1000;
$tres = oracle_fetch_array($tsql);
if($tres[0]>0){
$dsql = "delete from PRODDTA.F0411Z1 where VLEDUS = '".strtoupper($user)."' and VLEDBT = ".$nn[1]['NNN006']." and VLEDLN = " .$i*1000;
$count = oracle_update($dsql);
if($count === $tres[0]){
$count = $count;
}
}
$an8_sql = 'SELECT aban85 FROM PRODDTA.F0101 WHERE aban8='.$myArr[$i]['carrierNbr'];
$aban85 = oracle_fetch_array($an8_sql);
$dp = date_parse($myArr[$i]['ivd']);
$dp1 = getDate(mktime(0,0,0,$dp['month'],$dp['day'],$dp['year']));
$ivd = date2jul($dp1);//date('Y-M-d',mktime(0,0,0,$dp['month'],$dp['day'],$dp['year'])));
$dp = date_parse($myArr[$i]['dgl']);
$dp1 = getDate(mktime(0,0,0,$dp['month'],$dp['day'],$dp['year']));
$inv_no = strval($myArr[$i]['carrierInvoice']);
// index: ("VLEDUS", "VLEDBT", "VLEDTN", "VLEDLN")
$gld = date2jul($dp1);//date('Y-M-d',mktime(0,0,0,$dp['month'],$dp['day'],$dp['year'])));
$F0411Z1[1]['VLEDUS'] = '\''.strtoupper($user).'\'';//$_SESSION['userid'];
$F0411Z1[1]['VLEDLN'] = $i*1000;
$F0411Z1[1]['VLEDBT'] = $nn[1]['NNN006'];
$F0411Z1[1]['VLAN8'] = $myArr[$i]['carrierNbr'];
$F0411Z1[1]['VLPYE'] = $aban85[1]['ABAN85'];//$myArr[$i]['carrierNbr'];
$F0411Z1[1]['VLDIVJ'] = $ivd;//$myArr[$i]['ivd'];
//$F0411Z1[1]['VLDSVJ'] = $jul;
$F0411Z1[1]['VLDGJ'] = $gld;
$F0411Z1[1]['VLCO'] = $co;
$F0411Z1[1]['VLKCO'] = $co;
$F0411Z1[1]['VLAG'] = round(($myArr[$i]['allocation']*100),0);
$F0411Z1[1]['VLAAP'] = round(($myArr[$i]['allocation']*100),0);
$F0411Z1[1]['VLVINV'] = $inv_no;// <-- This element is the issue
$F0411Z1[1]['VLRMK'] = (strlen($carrName)>30?substr($carrName,0,29):$carrName);
$F0411Z1[1]['VLGLBA'] = '00573714';
$F0411Z1[1]['VLMCU'] = '1';
$F0411Z1[1]['VLTORG'] = $userid;//$_SESSION['userid'];
$F0411Z1[1]['VLUSER'] = $userid;//$_SESSION['userid'];
$F0411Z1[1]['VLPID'] = 'TRUCK';
$F0411Z1[1]['VLUPMJ'] = $jul;
$F0411Z1[1]['VLUPMT'] = $tjul;
$F0411Z1[1]['VLJOBN'] = 'TRUCK';
$F0411Z1[1]['VLURAB'] = $id;
$F0411Z1[1]['VLURRF'] = $myArr[$i]['bol'];
$z=1;
for($x=1;$x<=$F0411Z1[0];$x++){
$val1 = $F0411Z1[$x];
// first element of array is the counter, skip it
if($val1 != 1){
foreach($F0411Z1[1] as $val){
if($z==1){
$stmt = 'VALUES('.$val;
$z=99;
}
else{
if(!is_numeric($val))$val = '\''.$val.'\'';
$stmt .= ','.$val;
}
}
$stmt .= ')';
//$msSQL = 'INSERT INTO PS_PRODUCTION.PRODDTA.F0411Z1 '.$stmt;
$msSQL = 'INSERT INTO PRODDTA.F0411Z1 '.$stmt;
$count = oracle_update($msSQL);
if($count != 1) return 36;
}
}
}
}
else return 36;
return 0;
}
You can use the strval() method to cast the number as a string.
$number = 123E456;
$string = strval($number);
Or just force it to cast as a string
$string = (string) $number;

PHP Number Ticket System?

So, I'm trying to make a ticket system. So a users will be assigned 1000 tickets each, I will then generate a number from 1 to 5000, I then need it to select the user.
The way I have done it was made an array then looped 5000 times and assigned each user a ticket, however this doesn't work with really big numbers like 5,000,000. So I'm trying to think of the best way to do this and I'm unsure how.
Any advice?
$users = array();
$ticketNum = 0;
$result = $MySQL->query("SELECT * FROM `users` ");
while($row = $result->fetch_assoc()){
$i = 0;
while($i < $row['points']){
$i++;
$ticketNum++;
$ar = array("ticketNum" => $ticketNum, "username" => $row['username']);
array_push($users, $ar);
}
}
$winningTicket = 2500;
foreach($players as $ar){
if($winningTicket == $ar['ticketNum']){
//winner
}
}
I'll need more code to suggest a perfect solution, and also know why you want to assign 1000 tickets to a person?
But you could assign a from-to key instead of looping ALL of the numbers through, like
$users[$userNumber]["from"] = 1;
$users[$userNumber]["to"] = 6;
$findNumber = rand(1,6);
$foundUser = false;
foreach($users as $userNumber => $user) {
if ($user["from"] <= $findNumber && $user["to"] >= $findNumber) {
$foundUser = $user;
break;
}
}
if ($foundUser) {
print "Hooray, someone just got a ticket.";
}
but it does sound like a job you would solve in a different manner, maybe through your database.
Edit: I wrote the above before you added your code example, In your case I would probably do the following.
$result = $MySQL->query("SELECT username FROM users ORDER BY RAND() LIMIT 1");
$winningUser = $result->fetch_assoc();
print "We got a winner: ".$winningUser["username"];

Performance, check if value exists array or query

I need to check if a value exists in my database
I have a table where every user has an unique code. For example: 5h27f.
These values and users add up very quickly. So very soon I might have +2000 unique codes. What's the best, fastest and most efficient way to check if a value is unique?
foreach ($users as $user) {
$is_unique = FALSE;
while ($is_unique == FALSE) {
$code = unique_code();
$query = "SELECT * FROM unique_code_table WHERE code='$code';";
$res = $mysqli->query($query);
if ($res->num_rows > 0 {
} else {
$is_unique = TRUE;
}
}
}
OR
$query = "SELECT code FROM unique_code_table;";
$res = mysqli->query($query);
$codes = array();
$i = 0;
while ($row = $res->fetch_object()) {
$codes[$i] = $row->code;
$i++;
}
$code = unique_code();
while (in_array($code, $codes) {
$code = unique_code();
}
(this code might not be 100% accurate, I've written this just to explain the purpose of the question.)
I'd say that one query trip to the database vs. potentially 2000+ is significantly better to do. Second script will be significantly faster.
On the first code a LIMIT 1 would do wonders but compared to the second query it will pale as far as benchmarks are concerned.
Put the following at the bottom of your script to fine tune and benchmark:
PHP 5.4 +
$sParseTime = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
echo $sParseTime;

Updating a row number which has been randomised

So I currently have a random number being generated in PHP and I want to know how I go about updating the row number in my selected table. Code below:
$sxiq = mysql_query("SELECT * FROM `starting_eleven` WHERE `team_id`=$uid");
$sxir = mysql_fetch_row($sxiq);
$first = rand(1,11);
$stat_changed = rand(11,31);
$up_or_down = rand(1,2);
if ($up_or_down == 1) {
$player_name = explode(" ", $sxir[$first]);
$fn = $player_name[0];
$ln = $player_name[1];
$statq = mysql_query("SELECT * FROM `players` WHERE `first_name`=$fn AND `last_name`=$ln AND `user_id`=".$_SESSION['user_id']);
$statr = mysql_fetch_row($statq);
$stat = $statr[0];
}
I would like to update the row $stat_changed from the database, but I'm not sure if this is possible without doing a long if statement, telling the code if $stat_changed = 13 $stat = pace or something along those lines, but if this is the way it must be done then I'll have to. Just thought I'd see if there was any other simpler ways of doing this.
Thanks in advance
if ($stat_changed == 13) {
//insert UPDATE statement here
}

Script to update mysql not working

Okay so I have a PHP script that makes a user an artist if vote is high enough. The first part of the script works (the part that does the voting). However, the second part of the script that makes a user an artist does not. It worked before on localhost but is not working on live server for some reason. Either the script has changed and I didn't notice it or there's something wrong with my server config.
I know I should be using mysqli but please don't mention that I am working on it.
To explain how the system works, a form on the voting page is posted to this script and it all runs from there.
There is no error in the error log. Updating the table for //make an artist if vote high enough just doesn't work.
Here's the script:
<?php
session_start();
include("../database.php");
$username = $_SESSION["username"];
$artistname = htmlspecialchars(mysql_real_escape_string($_POST['artistname']));
$trackname = htmlspecialchars(mysql_real_escape_string($_POST['trackname']));
$trackurl = htmlspecialchars(mysql_real_escape_string($_POST['trackurl']));
$flag = 0; // Safety net, if this gets to 1 at any point in the process, we don't upload.
if(isset($_POST['yes'])){
//code runs if vote is yes
//check if user hasnt already voted on track
$result = mysql_query("SELECT username FROM voted WHERE voted='$artistname' AND trackname='$trackname' AND username='$username'")or die(mysql_error());
$check2 = mysql_num_rows($result);
if ($check2 != 0) {
echo('<t1>Sorry, you have already voted on this track. <b>Click next track.</b> </t1>');
$flag = $flag + 1;
}
//code runs if everything is okay
if($flag == 0){
mysql_query("UPDATE members SET vote = vote+1 WHERE artistname='$artistname'
");
echo '<t1><b>You liked the track "'.$trackname.'" by "'.$artistname.'"</t1></b>';
mysql_query("INSERT INTO voted (username, voted,trackname, yesno)
VALUES ('".$username."','".$artistname."','".$trackname."', 'yes')")
or die(mysql_error());
//make an artist if vote high enough
$vote = mysql_query("SELECT vote FROM members WHERE artistname='$artistname'")or die(mysql_error());
if ($vote > 50) {
$artisturl = htmlspecialchars(mysql_real_escape_string(str_replace(' ', '',$_POST['artistname'])));
mysql_query("UPDATE members SET artist='Y', image1='../files/noprofile.jpg', artisturl='$artisturl' WHERE artistname='$artistname'
")or die(mysql_error());
mysql_query("UPDATE tracks SET artist='Y', artisturl='$artisturl' WHERE artistname='$artistname'
")or die(mysql_error());
//email user that has just been made artist
$result = mysql_query("SELECT * FROM members WHERE artistname= '$artistname'");
while($row = mysql_fetch_array($result)){
function spamcheck($field)
{
//filter_var() sanitizes the e-mail
//address using FILTER_SANITIZE_EMAIL
$field=filter_var($row['email'], FILTER_SANITIZE_EMAIL);
//filter_var() validates the e-mail
//address using FILTER_VALIDATE_EMAIL
if(filter_var($row['email'], FILTER_VALIDATE_EMAIL))
{
return TRUE;
}
else
{
return FALSE;
}
}
{//send email
$to = $row['email'];
$subject = "Congratulations! You're now an NBS artist";
$message = "Hi ".$row['artistname'].",
//message removed for condensed code
$from = "";
$headers = 'From:' . "\r\n" .
'Reply-To: ' . "\r\n";
mail($to,$subject,$message,$headers);
}
}
echo '<br><t1>You just made "'.$artistname.'" an artist! <b>Click here</b> to see their profile.</t1>';
}
}
}
You are missing two lines to convert the resource returned by mysql_query() into an integer for the comparison with 50.
$vote = mysql_query("SELECT vote FROM members WHERE artistname='$artistname'")or die(mysql_error());
// Add these two lines
$vote = mysql_fetch_assoc($vote);
$vote = $vote['vote'];
if ($vote > 50) {
...however, all that section could be re-written to use 2 queries instead of 4:
//make an artist if vote high enough
$artisturl = mysql_real_escape_string(htmlspecialchars(str_replace(' ', '',$_POST['artistname'])));
// This effectively combines the first SELECT and the two UPDATEs into one query
$result = mysql_query("
UPDATE members m
LEFT JOIN tracks t ON m.artistname = t.artistname
SET
m.artist = 'Y',
t.artist = 'Y',
m.image1 = '../files/noprofile.jpg',
m.artisturl = '$artisturl',
t.artisturl = '$artisturl'
WHERE m.artistname = '$artistname' AND m.vote > 50
") or die(mysql_error());
// If this affected more than 0 rows, the user was made an artist
if (mysql_affected_rows($result) > 0) {
//email user that has just been made artist
$result = mysql_query("SELECT * FROM members WHERE artistname= '$artistname'");
// ...and so on
Note also that you should pass data through mysql_real_escape_string() as the last operation. So it should go mysql_real_escape_string(htmlspecialchars($data)) rather than the other way around.
I'll throw a dart at this one.
$vote = mysql_query("SELECT vote FROM members WHERE artistname='$artistname'")or die(mysql_error());
if ($vote > 50) {
I don't believe you are converting your mysql_query result into a useful variable. Maybe you were using mysql_fetch_assoc or mysql_num_rows ? Num rows makes more sense if you have an individual record for each vote. If you are summing them up then you can use something like
$output = mysql_fetch_assoc(mysql_query("SELECT vote FROM members WHERE artistname='$artistname'")or die(mysql_error());
$vote = $output['vote'];
Something else to point out is that you aren't using mysql_real_escape_string on your inputs. This is very dangerous and it is strongly encouraged to use this function if you are facing the public internet.

Categories