results table in php - php

I am having trouble understanding how a loop I have created works.
I have input fields on another page, which where results are sent to the results table in my database, with team name, team score, opposition score, opposition name.
Now the page where the results are entered mirrors the current contents of the results table so that previously entered scores appear already in the correct input field, and the ones with no socres enetered are at 0.
This page works perfectly in that a score that is edited, replaces the score in the results table, blank ones are left blank etc.
Howwever, I need to use these results to update my league table table in my database.
Currently I have a rather large loop, which (after I have fetched team name, team score, opposition score, opposition name, from the results table), works out how many points to give that team, and the opposing team, plus how many to add to the 'win' column and the 'loss' column etc of my league table.
The problem I have is that it will only ever do one result per team, because on each iteration of the loop, as soon as it finds a matching if statement (ie if team_score>opposition score) it updates the table, before running through the other results to find that teams results.
Because an edited result needs to be treated as an edited result, not an additional result, this is the only way I have been able to find to get even close.
here is a snippet of the code. There are many more if statementswithin the for loop but they are not needed to describe the problem I am having.
$query = $database->query("SELECT team_name, team_score, opposition_score, opposition_name from results_a");
while ($row = $query->fetch(PDO::FETCH_NUM)) {
$team[] = ($row[0]);
$team_score[] = ($row[1]);
$opposition_score[] = ($row[2]);
$opposition[] = ($row[3]);
}
$count=count($team);
for ($i = 0; $i < $count; $i++) {
$team_bonus[$i] = $team_score[$i] / 2;
$opposition_bonus[] = $opposition_score[$i] / 2;
//TEAM WINS, NO OPPOSITION BONUS
else if (($team_score[$i] > $opposition_score[$i]) && ($team_bonus[$i] > $opposition_score[$i])) {
$team_points[]+=3;
$team_win[]+=1;
$team_draw[]+=0;
$team_loss[]+=0;
$team_extra[]+=0;
$opposition_points[]+=0;
$opp_win[]+=0;
$opp_draw[]+=0;
$opp_loss[]+=1;
$opp_extra[]+=0;
$team_played[]+=1;
$opposition_played[]+=1;
}
$team_gd[] = $team_score[$i] - $opposition_score[$i];
$opposition_gd[] = $opposition_score[$i] - $team_score[$i];
//UPDATE LEAGUE TABLE
$query = $database->query("UPDATE pool_a SET played='$team_played[$i]',
win='$team_win[$i]',
draw='$team_draw[$i]',
loss='$team_loss[$i]',
goals_for='$team_score[$i]',
goals_against='$opposition_score[$i]',
goal_difference='$team_gd[$i]',
bonus_points='$team_extra[$i]',
points='$team_points[$i]'
where team_name = '$team[$i]'");
$query2 = $database->query("UPDATE pool_a SET played='$opposition_played[$i]',
win='$opp_win[$i]',
draw='$opp_draw[$i]',
loss='$opp_loss[$i]',
goals_for='$opposition_score[$i]',
goals_against='$team_score[$i]',
goal_difference='$opposition_gd[$i]',
bonus_points='$opp_extra[$i]',
points='$opposition_points[$i]'
where team_name='$opposition[$i]'");
The+= within the if statement are useless, because instead of going to the next row where the team name is the first teams name, it runs the query, then when that teams name appears again, it replaces the data next time the query runs.
Very stuck, have spent a lot of time on this!
If you need any more info let me know.
Many thanks

Related

PHP & MYSQL Select dropdown not filtering correctly on all results

I'm trying to make my select dropdown filter correctly on a MySQL query although to no avail. If a holiday date is equal to a stocktaking date, the worker should not be visible on the dropdown.
This works only for the first result of the row, below are some examples;
More examples;
User A can't work 10/6/2019 so he doesn't show (this is correct)
and User A also can't work 11/6/2019 but now shows for all dates. (this shouldn't be happening)
Another example;
User A can't work 10/6/2019 so he doesn't show (this is correct)
and User B can't work 11/6/2019 but now User A shows for all dates again. (this shouldn't be happening)
Another example;
User A & B can't work 10/6/2019 so User B & User A don't show (this is correct)
and User B can't work 11/6/2019 but now User A shows for all dates again. (this shouldn't be happening)
if ($available_date == $stk_date) {
$query = "
SELECT *
FROM user_master
WHERE id NOT IN (
SELECT UM.id
FROM user_master UM
JOIN bookings B ON B.id_item = UM.id
JOIN stocktakes S ON B.the_date = S.stk_date
)
";
$result = mysqli_query($connect, $query);
// $row = mysqli_fetch_array($result);
if ($result) {
while ($row = mysqli_fetch_array($result)){
echo "<option value=$row[first_name]>$row[first_name] $row[last_name]</option>'";
}
}
}
else {
$query = "SELECT * FROM user_master";
$result = mysqli_query($connect, $query);
// $row = mysqli_fetch_array($result);
if ($result) {
while ($row = mysqli_fetch_array($result)){
echo "<option value=$row[first_name]>$row[first_name] $row[last_name]</option>'";
}
}
}
I need to figure out to make it so that;
If User A, B, C or D etc - can't work on 10/6/2019 - they don't show in any of the dropdowns for the 10/6/2019 row.
and if User A, B & C - can't work on 11/6/2019 - they don't show in any of the dropdowns for 10/6/2019 row and 11/6/2019 as they're marked as not working those days.
Here's some SQL Fiddles (first time using it, wasn't sure how to make multiple tables) in there is just the basic data that I'm using.
Holidays - http://www.sqlfiddle.com/#!9/814d14/1
Stocktakes - http://www.sqlfiddle.com/#!9/5cc05e/1
Users - http://www.sqlfiddle.com/#!9/1a3ff6/1
Update:
I've noticed it seems to work if there's only one 'value' if that makes any sense, so for example;
If I make User A booked on 10/06/2019 it works.
But if I make User A booked on 10/06/2019 & 11/06/2019 it doesn't work.
If I make User A & User B booked 10/06/2019 it works
Although if I make User B booked on 11/06/2019 it doesn't work.
Update 2:
It 100% works as intended if there's only one row in the table - if a second row is added, it just doesn't work and mixes results up. Almost as if the query isn't running correctly for each row?

Randomising two teams Php Mysql

I am constructing a game where users join into a game lobby and then get split into two teams, and then answer questions.
But I have a problem, everything except the team randomiser is done.
I want the users to be assigned to a team when a button is pressed, which is not a problem, I can do this.
The problem is making the teams.
As it is now I'm getting and displaying the current users by this line of code
if($result = mysqli_query($link, $sql)){
if(mysqli_num_rows($result) > 0){
while($row = mysqli_fetch_array($result)){
echo utf8_encode("<div><br>". $row["name"]. " <b>| Lag : </b><span>" . $row["team"] . "</span><br></div>"); // för namn osv
mysqli_close($link);
}
}
}
echo "";
I want all users to randomly be assigned to two large teams equally, therefore I can not use a simple randomiser giving the user 50/50 chance to be on one or the other team because there is a chance of making the teams extremely unequal.
When the user has been assigned to a team, the script will run a query updating a column called "team" for each user and then the game will start.
How can I make it so that when a button is pressed, the code takes the users and puts all the users into two different teams?
Is this done with arrays, or what? I'm clueless!
It is important that the teams are as equal as possible.
What should I do?
I have seen things with javascript, but that doesn't work here.
clarifications.
There are no max players, there are two teams. I will run a query assigning the users to a team in the database.
If I have missed something, or if more information is needed, just tell me!
Once you've got the list players in the result set use the shuffle() function to shuffle them up, then assign the 1st half of the players to team 1 and the other half to team 2
Below assumes that there will be two teams with an even number of players:
<?php
$players = array("Player 1","Player 2","Player 3","Player 4","Player 5","Player 6","Player 7","Player 8");
//Players list before shuffling
var_dump($players);
shuffle($players);
//Players list after shuffling
var_dump($players);
// Get number of players per team
$player_count = count($players);
$players_per_team = $player_count/2;
$teams = array_chunk($players,$players_per_team);
$team_1 = $teams[0];
$team_2 = $teams[1];
echo 'Team 1';
var_dump($team_1);
echo 'Team 2';
var_dump($team_2);

How to handle/optimize thousands of different to executed SELECT queries?

I need to synchronize specific information between two databases (one mysql, the other a remote hosted SQL Server database) for thousands of rows. When I execute this php file it gets stuck/timeouts after several minutes I guess, so I wonder how I can fix this issue and maybe also optimize the way of "synchronizing" it.
What the code needs to do:
Basically I want to get for every row (= one account) in my database which gets updated - two specific pieces of information (= 2 SELECT queries) from another SQL Server database. Therefore I use a foreach loop which creates 2 SQL queries for each row and afterwards I update those information into 2 columns of this row. We talk about ~10k Rows which needs to run thru this foreach loop.
My idea which may help?
I have heard about things like PDO Transactions which should collect all those queries and sending them afterwards in a package of all SELECT queries, but I have no idea whether I use them correctly or whether they even help in such cases.
This is my current code, which is timing out after few minutes:
// DBH => MSSQL DB | DB => MySQL DB
$dbh->beginTransaction();
// Get all referral IDs which needs to be updated:
$listAccounts = "SELECT * FROM Gifting WHERE refsCompleted <= 100 ORDER BY idGifting ASC";
$ps_listAccounts = $db->prepare($listAccounts);
$ps_listAccounts->execute();
foreach($ps_listAccounts as $row) {
$refid=$row['refId'];
// Refsinserted
$refsInserted = "SELECT count(username) as done FROM accounts WHERE referral='$refid'";
$ps_refsInserted = $dbh->prepare($refsInserted);
$ps_refsInserted->execute();
$row = $ps_refsInserted->fetch();
$refsInserted = $row['done'];
// Refscompleted
$refsCompleted = "SELECT count(username) as done FROM accounts WHERE referral='$refid' AND finished=1";
$ps_refsCompleted = $dbh->prepare($refsCompleted);
$ps_refsCompleted->execute();
$row2 = $ps_refsCompleted->fetch();
$refsCompleted = $row2['done'];
// Update fields for local order db
$updateGifting = "UPDATE Gifting SET refsInserted = :refsInserted, refsCompleted = :refsCompleted WHERE refId = :refId";
$ps_updateGifting = $db->prepare($updateGifting);
$ps_updateGifting->bindParam(':refsInserted', $refsInserted);
$ps_updateGifting->bindParam(':refsCompleted', $refsCompleted);
$ps_updateGifting->bindParam(':refId', $refid);
$ps_updateGifting->execute();
echo "$refid: $refsInserted Refs inserted / $refsCompleted Refs completed<br>";
}
$dbh->commit();
You can do all of that in one query with a correlated sub-query:
UPDATE Gifting
SET
refsInserted=(SELECT COUNT(USERNAME)
FROM accounts
WHERE referral=Gifting.refId),
refsCompleted=(SELECT COUNT(USERNAME)
FROM accounts
WHERE referral=Gifting.refId
AND finished=1)
A correlated sub-query is essentially using a sub-query (query within a query) that references the parent query. So notice that in each of the sub-queries I am referencing the Gifting.refId column in the where clause of each sub-query. While this isn't the best for performance because each of those sub-queries still has to run independent of the other queries, it would perform much better (and likely as good as you are going to get) than what you have there.
Edit:
And just for reference. I don't know if a transaction will help here at all. Typically they are used when you have several queries that depend on each other and to give you a way to rollback if one fails. For example, banking transactions. You don't want the balance to deduct some amount until a purchase has been inserted. And if the purchase fails inserting for some reason, you want to rollback the change to the balance. So when inserting a purchase, you start a transaction, run the update balance query and the insert purchase query and only if both go in correctly and have been validated do you commit to save.
Edit2:
If I were doing this, without doing an export/import this is what I would do. This makes a few assumptions though. First is that you are using a mssql 2008 or newer and second is that the referral id is always a number. I'm also using a temp table that I insert numbers into because you can insert multiple rows easily with a single query and then run a single update query to update the gifting table. This temp table follows the structure CREATE TABLE tempTable (refId int, done int, total int).
//get list of referral accounts
//if you are using one column, only query for one column
$listAccounts = "SELECT DISTINCT refId FROM Gifting WHERE refsCompleted <= 100 ORDER BY idGifting ASC";
$ps_listAccounts = $db->prepare($listAccounts);
$ps_listAccounts->execute();
//loop over and get list of refIds from above.
$refIds = array();
foreach($ps_listAccounts as $row){
$refIds[] = $row['refId'];
}
if(count($refIds) > 0){
//implode into string for use in query below
$refIds = implode(',',$refIds);
//select out total count
$totalCount = "SELECT referral, COUNT(username) AS cnt FROM accounts WHERE referral IN ($refIds) GROUP BY referral";
$ps_totalCounts = $dbh->prepare($totalCount);
$ps_totalCounts->execute();
//add to array of counts
$counts = array();
//loop over total counts
foreach($ps_totalCounts as $row){
//if referral id not found, add it
if(!isset($counts[$row['referral']])){
$counts[$row['referral']] = array('total'=>0,'done'=>0);
}
//add to count
$counts[$row['referral']]['total'] += $row['cnt'];
}
$doneCount = "SELECT referral, COUNT(username) AS cnt FROM accounts WHERE finished=1 AND referral IN ($refIds) GROUP BY referral";
$ps_doneCounts = $dbh->prepare($doneCount);
$ps_doneCounts->execute();
//loop over total counts
foreach($ps_totalCounts as $row){
//if referral id not found, add it
if(!isset($counts[$row['referral']])){
$counts[$row['referral']] = array('total'=>0,'done'=>0);
}
//add to count
$counts[$row['referral']]['done'] += $row['cnt'];
}
//now loop over counts and generate insert queries to a temp table.
//I suggest using a temp table because you can insert multiple rows
//in one query and then the update is one query.
$sqlInsertList = array();
foreach($count as $refId=>$count){
$sqlInsertList[] = "({$refId}, {$count['done']}, {$count['total']})";
}
//clear out the temp table first so we are only inserting new rows
$truncSql = "TRUNCATE TABLE tempTable";
$ps_trunc = $db->prepare($truncSql);
$ps_trunc->execute();
//make insert sql with multiple insert rows
$insertSql = "INSERT INTO tempTable (refId, done, total) VALUES ".implode(',',$sqlInsertList);
//prepare sql for insert into mssql
$ps_insert = $db->prepare($insertSql);
$ps_insert->execute();
//sql to update existing rows
$updateSql = "UPDATE Gifting
SET refsInserted=(SELECT total FROM tempTable WHERE refId=Gifting.refId),
refsCompleted=(SELECT done FROM tempTable WHERE refId=Gifting.refId)
WHERE refId IN (SELECT refId FROM tempTable)
AND refsCompleted <= 100";
$ps_update = $db->prepare($updateSql);
$ps_update->execute();
} else {
echo "There were no reference ids found from \$dbh";
}

Insert multiple rows in MySQL and check for random string without long delay (~80 rows each minute)

For a research project I am obtaining data from a local bus company's GPS system (through their API). I created a php cron job that runs every minute to obtain data like the vehicle, route ID, location, destination, etc. The data did not contain a unique "run number" for each bus route (a unique number so that I can track the progression of a single bus along its route), so I created my own that checks if the vehicle ID, destination, and relative time are similar, and assigns the unique "run ID" to it so that I can track the bus along its route. If no run ID exists, a random one is generated. (Any vehicle with the same "vid" and "pid" within 2 minutes of the last inserted row "timeadded" is on the same run, and this is important for my research)
Each time the cron runs (1 minute), approximately 80 rows are added into the database.
Initially the job would run quickly. However, with over 500,000 rows now, I've noticed the job can take upwards of 40 seconds. I believe it's because for each of the ~80 rows, it has to check the entire table ("vehicles") to see if the same run ID exists, essentially querying a large table and inserting a row 80 times. I want to get at least a week's worth of data (on day 4 now), at which point I can export the data, erase all rows, and start over. My question is: Is there any way I can refactor my PHP/SQL code to make the process run faster? It's been years since I've worked with SQL, so I'm sure there's a more ingenious way to insert all this data.
<?php
// Obtain data from XML
$xml = simplexml_load_file("url.xml");
foreach ($xml->vehicle as $vehicle) {
$vid = $vehicle->vid;
$tm = $vehicle->tmstmp;
$dat = substr($vehicle->tmstmp, 0, 8);
$tme = substr($vehicle->tmstmp, 9);
$lat = $vehicle->lat;
$lon = $vehicle->lon;
$hdg = $vehicle->hdg;
$pid = $vehicle->pid;
$rt = $vehicle->rt;
$des = $vehicle->des;
$pdist = $vehicle->pdist;
// Database connection and insert
mysql_connect("redacted", "redacted", "redacted") or die(mysql_error()); mysql_select_db("redacted") or die(mysql_error());
$sql_findsim = "SELECT vid, pid, timeadded, run, rt FROM vehicles WHERE vid=" . mysql_real_escape_string($vid). " AND pid=" . mysql_real_escape_string($pid). " AND rt=" . mysql_real_escape_string($rt). " AND timeadded > DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 2 MINUTE);";
$handle = mysql_query($sql_findsim);
$row = mysql_fetch_row($handle);
$runid = $row[3];
if($runid !== null) {
$run = $runid;
} else {
$run = substr(md5(rand()), 0, 30);
}
$sql = "INSERT INTO vehicles (vid, tmstmp, dat, tme, lat, lon, hdg, pid, rt, des, pdist, run) VALUES ($vid,'$tm','$dat','$tme','$lat','$lon',$hdg,$pid,'$rt','$des',$pdist,'$run')";
$result = mysql_query($sql);
mysql_close();
}
?>
Thanks for any help with refactoring this code to get it to run more quickly and efficiently.
Do you have any indexes on the table? A compound index on (vid,pid,rt,timeadded) will make the query faster, avoiding a full table scan.
create index fastmagic on vehicles (vid,pid,rt,timeadded)
Alternatively, you could skip the select all together and just to the insert without assigning the "run" random value. This will keep your cron job at "constant time" since all you're doing is appending new data.
After you've got your week of data go back and write "second pass" code to step through each row (select * from vehicle order by timeadded). For each row, do your "select" similar to how you've already done it - then "update" the row you are processing now.
If you go with the alternate, you'll probably want an autoincrement "id" integer column to make row identification clearer (if you don't already have one).
I would suggest that,
Create a table as vehicle_ids ( or some meaningful name ) these fields.
vid, pid, run, rt
instead of checking in vehicles table for vid, you can check the above table for id, if not insert ( make vid as auto increment ).
Normalize your table and also index your vehicle table

What's the most efficient way to pull the data from mysql so that it is formatted as follows:

I have a table that has patient information (name, dob, ssn, etc.) and a table that has lists of medications that they take. (aspirin, claritin, etc.) The tables are related by a unique id from the patient table. So, it's easy enough to pull all of Mary Smith's medications.
But, what I need to do is to show a paginated list of patients that shows their name, other stuff from the patient table and has a column with a line-separated list of their medications. Roughly, this:
If I do a simple left join, I get 3 repeated rows of Mary Smith with one medication per row.
The patient table can have thousands of records, so I don't want to do a query to get all the patients and then loop through and get their meds. And, because it's paginated based on patient, I can't figure out how to get the correct number of patients for the page, along with all their medications.
(The patients/medications thing is just a rough example of the data; so please don't suggest restructuring how the data is stored.)
GROUP_CONCAT to the rescue!
SELECT patients.first_name, patients.last_name, GROUP_CONCAT(prescriptions.medication SEPARATOR ", ") AS meds FROM patients LEFT JOIN prescriptions ON prescriptions.patient_id = patients.id GROUP BY patients.id;
You've got a few choices.
rowspan clauses with one drug per cell per user. You'd need to run two SQL queries to precalculate how big each user's span would have to be, or suck the query results into PHP and do the counting there.
Simple state machine - start a new row each time the user changes, then just keep adding more drug names seperated with <br /> while the user's name stays constant.
The second one's probably easiest:
$previous_name = null;
$first = true;
echo "<table";
while($row = mysql_fetch_assoc($results)) {
if ($row['name'] <> $previous_name) {
if (!$first) {
echo "</td></tr>"; // end previous row, if it's not the first row we've output
$first = false;
}
echo "<tr><td>$row[name]</td><td>"
$previous_name = $row['name'];
}
echo "$row['drug']<br />";
}
echo "</td></tr></table>";
I think what you are looking for is referred to a 'concation of subquery'.
Check http://forums.mysql.com/read.php?20,157425,157796#msg-157796 and http://mysql.bigresource.com/SELECT-CONCAT-Subquery-S5cIpzqO.html

Categories