Very slow PHP - MySQL script - php

I am new at using PHP-MySQL. I have two MySQL tables:
Concreteness: A table that contains concreteness scores for 80K words
Brian: A table with 1 million rows, each containing one or two words.
I have a small PHP script that takes each row in "Brian", parses it, looks for the scores in "Concreteness" and records it in "Brian."
I have been running this script with several other tables that had 300-400k rows with each hundreds of words. "Brian" is different because it has 1 million rows with 1 or 2 words per row. For some reason, my script is SUPER slow with Brian.
Here is the actual script:
<?php
include "functions.php";
set_time_limit(0); // NOTE: no time limit
if (!$conn)
die('Not connected : ' . mysql_error());
$remove = array('{J}','{/J}','{N}','{/N}','{V}','{/V}','{RB}','{/RB}'); // tags to remove
$db = 'LCM';
mysql_select_db($db);
$resultconcreteness = mysql_query('SELECT `word`, `score` FROM `concreteness`') or die(mysql_error());
$array = array(); // NOTE: init score cache
while($row = mysql_fetch_assoc($resultconcreteness))
$array[strtolower($row['word'])] = $row['score']; // NOTE: php array as hashmap
mysql_free_result($resultconcreteness);
$data = mysql_query('SELECT `key`, `tagged` FROM `brian`') or die(mysql_error()); // NOTE: single query instead of multiple
while ($row = mysql_fetch_assoc($data)) {
$key = $row['key'];
$tagged = $row['tagged'];
$weight = $count = 0;
$speech = explode(' ', $tagged);
foreach ($speech as $word) {
if (preg_match('/({V}|{J}|{N}|{RB})/', $word, $matches)) {
$weight += $array[strtolower(str_replace($remove, '', $word))]; // NOTE: quick access to word's score
if(empty($array[strtolower(str_replace($remove, '', $word))])){}else{$count++;}
}
}
mysql_query('UPDATE `brian` SET `weight`='.$weight.', `count`='.$count.' WHERE `key`='.$key, $conn) or die(mysql_error());
// Print out the contents of the entry
Print "<b>Key:</b> ".$info['key'] . " <br>";
}
mysql_free_result($data);
?>

I guess the real problem is the 1 million mysql update statements you fire to the database. Consider bundling the update statements (and also remove the print):
$i=0;
while ($row = mysql_fetch_assoc($data)) {
// ... left out the obvious part
$sql .= "'UPDATE `brian` SET `weight`='.$weight.', `count`='.$count.' WHERE `key`='.$key;";
$i++;
if ($i%1000 == 0) {
mysql_query($sql) or die(mysql_error());
$i=0;
$sql = "";
}
}
// remember to save the last few updates
mysql_query($sql) or die(mysql_error());

Related

How to compare tables from different mySQL databases with PHP?

I'm new to PHP as well as the field and have been tasked with finding inconsistencies amongst tables from different mySQL databases. Every table should be the same between databases (column names, column count), with the exception of the actual data held in it, which they are not. I will need to identify the offending columns, the table they are in and, the database they are in. Below is my code thus far:
<?php
chdir(<path>);
include(<file>);
include(<file>);
$db = new db($hostname, $user, $pass, $commondatabase);
// Get baseline DB columns
//$db->select_db('<baseDB>');
//$btq = "SHOW TABLES";
//$btr = $db->query($btq);
//while($trows = $db->fetch_assoc($btr) {
// $bcq = "SHOW COLUMNS FROM ".$btr[<key>];
// $bcr = "$db->query($bcq);
// $basecolumns[] = $bcr;
//}
$dbq = "SELECT * FROM <commonDB>.<DBnames> ORDER BY <DBnamesID>";
$dbr = $db->query($dbq);
while($dbrow = $db->fetch_assoc($dbr)) {
echo "\n";
print_r($dbrow['dbname']);
echo "\n";
$db->select_db($dbrow['dbname']);
$tq = "SHOW TABLES";
$tr = $db->query($tq);
while($table = $db->fetch_assoc($tr)) {
/*print_r($dbtables);*/
$cq = "SHOW COLUMNS FROM ".$table['Tables_in_'.$dbrow['dbname']];
$cr = $db->query($cq);
$dbcols = [];
while($col = $db->fetch_assoc($cr)) {
/*print_r($col);*/ $dbcols = $col;
// Do check against baseline here
//if($dbcols != $basecolumns) {
// $badcolumns[] = $dbcols;
//}
}
}
}
$db->close();
?>
Currently it will loop down to the columns, but the output is not visually pleasing nor very manageable. My first concern is to get this working to where I can loop down to the columns and actually get them in their own arrays to be checked against the baseline and I've hit a wall, so any direction would be much appreciated. Currently each column is being assigned to its own array versus an array of all the column names for the database and table the loop is on. TIA!
Here is what I came up with. Not sure if it's the most DRY way to go about it, but it works and achieves the results I was looking for. Thank you to Barmar for sending me down the right path.
<?php
chdir(<path>);
include(<file>);
include(<file>);
$db = new db($hostname,$user,$pass,$commondatabase);
$db->select_db(<database>);
// Get tables from baseline database
$q="select distinct <column-name> from <table-name> where <where-param> ORDER BY <order-by-param>";
$r=$db->query($q);
while($tables=$db->fetch_assoc($r)) {
$table = $tables[<key>];
$x = array();
$x["$table"]=array();
// Get table columns from baseline database
$q="select <column-name> from <table-name> where <where-param> and <where-param> ORDER BY <order-by-param>";
$r2=$db->query($q);
while ($columns=$db->fetch_assoc($r2)) {
$x["$table"][]=$columns[<key>];
}
// Check other databases for this table
$q="select * from $commondatabase.<table-with-database-names> ";
$q.="where <where-param> order by <order-by-param>";
$r2=$db->query($q);
while ($databases=$db->fetch_assoc($r2)) {
$y = array();
$y["$table"]=array();
// Get table columns in $databases[<key>]
$q="select <column-name> from <table-name> where <where-param> and <where-param> ORDER BY <order-by-param>";
$r3=$db->query($q);
while ($columns=$db->fetch_assoc($r3)) {
$y["$table"][]=$columns[<key>];
}
// Check against baseline
$z1 = array_diff($x["$table"],$y["$table"]);
$z2 = array_diff($y["$table"],$x["$table"]);
// Echo out comparison results
if (empty($z1) && empty($z2)) {
//echo "all good.\n";
} else {
echo "Difference found in {$databases[<key>]}.{$tables[<key>]}:";
if (!empty($z1)) print_r($z1);
if (!empty($z2)) print_r($z2);
}
}
}
$db->close();
?>

Grouping Mysql PHP Query Results

I have a basic PHP page which searches a MySQL Database of retro ZX Spectrum magazine articles.
The address is [www.retroresource.co.uk][1]
The PHP Results Page is fine, in that it produced the results, but I would like to be able to group the results by 'fgames.fgname'
So for example if I searched for Dizzy
Dizzy (as a header)
Table with rows that have fgames.fgname = Dizzy
Spindizzy (as a header)
Table with rows that have fgames.fgname = Spindizzy
Treasure Island Dizzy (as a header)
Table with rows that have fgames.fgname = Treasure Island Dizzy
etc etc
I have googled and looked in a few of my text books, but am unable to see a solution. Any help much appreciated.
Peter
Amended Code after assistance (No results though)
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
require( '../connect_db.php' ) ;
$query = $_GET['query'];
$min_length = 4;
// you can set minimum length of the query if you want
$query = strip_tags($query);
$query = mysqli_real_escape_string ($dbc,$query);
$q = "SELECT fgames.fgname, fgames.fgprorg, fgames.fgprbud, fgames.fgratng, fgames.fgprdsk, ftypes.fttname, frefs.fryymm, frefs.frpage, frefs.frissue, fmagzne.fmname, frefcde.ffname
FROM fgames, ftypes,frefs, fmagzne,frefcde
WHERE ftypes.fttype = fgames.fgtype
and fgames.fglink = frefs.frlink2
and frefs.frentry = frefcde.ffentry
and frefs.frmag = fmagzne.fmmag
and fgames.fgname LIKE '%".$query."%'
order by fgames.fgname ASC" ;
$r = mysqli_query( $dbc , $q ) ;
$current_group = '';
while ( $row = mysqli_fetch_array( $r , MYSQLI_ASSOC ) )
{
if($row['fgames.fgname']) !== $current_group)
{
if($current_group !== '')
{
echo '</table>';
}
$current_group = $row['fgames.fgname'];
echo '<table><tr><th>FGNAME</th><th>FGPRORG</th><th>FGRBUD</th><th>FGRATNG</th><th>FGRDSK</th>
<th>FTTTNAME</th><th>FRYYMM</th><th>FRPAGE</th><th>FRISSUE</th><th>FMNAME</th><th>FFNAME</th></tr>';
}
echo '<tr><td>'.$row['fgname'].'</td><td>'.$row['fgprorg'].'</td><td> '.$row['fgprbud'].' </td><td>'.$row['fgratng'].' </td> <td>'.$row['fgprdsk'].' </td><td>'.$row['fttname'].'</td><td> '.$row['fryymm'].'</td><td> '.$row['frpage'].'</td><td> '.$row['frissue'].' </td> <td>'.$row['fmname'].' </td><td>'.$row['ffname'].'</td></tr>';
}
echo '</table>';
else
{
echo '<p>' . mysqli_error( $dbc ) . '</p>' ;
}
# Close the connection.
mysqli_close( $dbc ) ;
?>
There are two basic approaches.
Either load the query data into a two dimensional array and output with nested loop like this:
$array_for_output = array();
while($row = /* your query fetch mechanism here */) {
$array_for_output[$row['field_you_are_grouping_on']] = $row;
}
foreach($array_for_output as $table_name => $table_data) {
// output table header here
foreach($table_data as $table_row) {
// output individual row
}
// output table closure here
}
Or the other approach is to query using an ORDER BY clause on the field you are trying to group by and detect changes in the current group in single loop
$query = "
SELECT ...
FROM ...
WHERE ...
ORDER BY field_you_are_grouping_on, any_secondary_ordering_field_here ASC";
// execute query here
$current_group = '';
while ($row = /* your query fetch mechanism here */) {
if($row['field_you_are_grouping_on']) !== $current_group) {
// we have started a new group
if($current_group !== '') {
// this is not first table
// so output closure to previous table here
}
$current_group = $row['field_you_are_grouping_on'];
// output new table header here
}
// output row data here
}
// output closure to final table here
The second approach is usually preferable from a memory management standpoint (you don't have to store entire result set in memory at any point), but some beginning developers may not find this not as intuitive as the first method. The first method also may be more appropriate if you want to take the full result set and inject it into some sort of template that is used to render the display.

Reading a field from the code, working with the data, saving the values to new fileds in the row

So here is what I am doing.
Read a row each in for loop. (Because all at once is going to take some resources since I am in a shared hosting.)
2.Get the right field data to a variable.
3.Manipulate the req datas dependant on the extracted field.
4.update the new fields where filed=extracted data.
Bit of addition, I am adding the current position to a file, so that the script can continue from there next time it is run.
Problem : It doesnt seem to work. The counter.txt gets values like 3-4, but it simply resides there. my db has like 1000k rows.
my code :
require ("dbconnect.php");
header("refresh:29;url=process.php"); // so it doesnt ever end. I cant use max_execution_time here for some reason.
$count = mysql_query("SELECT COUNT(*) FROM collection ");
$data = mysql_fetch_array($count);
$count = $data[0];
echo $count;
$countfile = fopen("counter.txt", "r");
$counter = fgets($countfile);
echo fgets($countfile);
while (fgets($countfile) <= $count)
{
$i = fgets($countfile);
$takeword = mysql_query("SELECT word FROM collection WHERE id='$i'") or die();
$wd = mysql_fetch_array($takeword);
$data = $wd[0];
$d1 = hash($algorith='md2',$data);
$d2 = hash($algorith='md4',$data);
$write = mysql_query("UPDATE collection SET md2='$d1', md4='$d2' WHERE id='$i'") or die(mysql_error());
//opens, empties and write the new pointer to the file. closes, and open the file in readmode for the next read at the loop.
$counts = fopen("counter.txt", "w+");
fwrite($counts, $counter + 1);
fclose($counts);
$countfile = fopen("counter.txt", "r");
}
Any help would be appreciated :) Looking for code optimization and killing the error. Suggestions would do.:)
Alright I'd do something like this (sorry about the delayed response, I kept forgetting)
<?php
//main execution
$sql = mysql_connect(...);
if (!$sql)
die ("No database connection");
if (!mysql_select_db(..., $sql))
die ("Database does not exist in this schema");
//Run the query for this iteration.
processQuery();
//---
function getQueryOffset($file)
{
$offset = 0; //default offset
if (file_exists($file)) //check if the counter file exists
{
$contents = file_get_contents($file); //get the contents of the counter
if ($contents !== FALSE && is_numeric($contents)) //check if an appropriate counter value
$offset = intval($contents);
}
return $offset;
}
function processQuery()
{
$table = "collection"; //table to update
$counter = "counter.txt"; //where to look for the last execution's offset.
$maxrows = 10000; //update 10,000 rows each time this file is loaded.
$sql = $GLOBALS['sql'];
//calculate the number of rows in the table
$qCount = mysql_query("SELECT COUNT(*) max FROM $table", $sql);
$aCount = mysql_fetch_assoc($qCount);
mysql_free_result($qCount);
$max = $aCount["max"];
$offset = getQueryOffset($counter); //calculate the offset (or a default 0)
if ($offset < $max) //if offet >= max, we're done.
{
$qUpdate = mysql_query("SELECT word, id FROM $table LIMIT $offset, $maxrows", $sql); //get the next "maxrows" rows from the table.
if ($qUpdate)
{
$assoc = NULL;
while (($assoc = mysql_fetch_assoc($qUpdate)) != NULL)
{
$md4 = hash("md4", $assoc["word"]); //calculate the hashes
$md2 = hash("md2", $assoc["word"]);
$id = $assoc["id"]; //id the row
mysql_query("UPDATE $table SET md2='$md2', md4='$md4' WHERE id=$id", $sql); //update the table columns
}
//update the offset in the counter file.
file_put_contents($counter, ($offset + mysql_num_rows($qUpdate)));
mysql_free_result($qUpdate);
}
}
}
mysql_close($sql);
?>
1 issue that I am seeing here:
Check your update query - that seems to be wrong. According to me, it should be "SET md2='$d1' AND md4='$d2'"
Another issue that I am not sure about:
I am unsure if md2 and md4 are valid names of hashing algorithms
A better way of doing this:
1. Dont write to file!
2. Create an additional column in your SQL by the name 'status', default value to 0. On update, change that value to 1.
3. Search for rows to edit based on query "SELECT word FROM collection WHERE status=0 limit 0,1"
4. OR if the columns md2 and md4 are empty in the original table, query could also be "SELECT word FROM collection WHERE md2='' and md4='' limit 0,1"
Hope this helps.

php sql find and insert in empty slot

I have a game script thing set up, and when it creates a new character I want it to find an empty address for that players house.
The two relevant table fields it inserts are 'city' and 'number'. The 'city' is a random number out of 10, and the 'number' can be 1-250.
What it needs to do though is make sure there's not already an entry with the 2 random numbers it finds in the 'HOUSES' table, and if there is, then change the numbers. Repeat until it finds an 'address' not in use, then insert it.
I have a method set up to do this, but I know it's shoddy- there's probably some more logical and easier way. Any ideas?
UPDATE
Here's my current code:
$found = 0;
while ($found == 0) {
$num = (rand()%250)+1; $city = (rand()%10)+1;
$sql_result2 = mysql_query("SELECT * FROM houses WHERE city='$city' AND number='$num'", $db);
if (mysql_num_rows($sql_result2) == 0) { $found = 1; }
}
You can either do this in PHP as you do or by using a MySQL trigger.
If you stick to the PHP way, then instead of generating a number every time, do something like this
$found = 0;
$cityarr = array();
$numberarr = array();
//create the cityarr
for($i=1; $i<=10;$i++)
$cityarr[] = i;
//create the numberarr
for($i=1; $i<=250;$i++)
$numberarr[] = i;
//shuffle the arrays
shuffle($cityarr);
shuffle($numberarr);
//iterate until you find n unused one
foreach($cityarr as $city) {
foreach($numberarr as $num) {
$sql_result2 = mysql_query("SELECT * FROM houses
WHERE city='$city' AND number='$num'", $db);
if (mysql_num_rows($sql_result2) == 0) {
$found = 1;
break;
}
}
if($found) break;
}
this way you don't check the same value more than once, and you still check randomly.
But you should really consider fetching all your records before the loops, so you only have one query. That would also increase the performance a lot.
like
$taken = array();
for($i=1; $i<=10;$i++)
$taken[i] = array();
$records = mysql_query("SELECT * FROM houses", $db);
while($rec = mysql_fetch_assoc($records)) {
$taken[$rec['city']][] = $rec['number'];
}
for($i=1; $i<=10;$i++)
$cityarr[] = i;
for($i=1; $i<=250;$i++)
$numberarr[] = i;
foreach($cityarr as $city) {
foreach($numberarr as $num) {
if(in_array($num, $taken[]) {
$cityNotTaken = $city;
$numberNotTaken = $number;
$found = 1;
break;
}
}
if($found) break;
}
echo 'City ' . $cityNotTaken . ' number ' . $numberNotTaken . ' is not taken!';
I would go with this method :-)
Doing it the way you say can cause problems when there is only a couple (or even 1 left). It could take ages for the script to find an empty house.
What I recommend doing is insert all 2500 records in the database (combo 1-10 with 1-250) and mark with it if it's empty or not (or create a combo table with user <> house) and match it on that.
With MySQL you can select a random entry from the database witch is empty within no-time!
Because it's only 2500 records, you can do ORDER BY RAND() LIMIT 1 to get a random row. I don't recommend this when you have much more records.

VB if-statement in PHP

I need help to convert the following Visual Basic statement into a PHP equivalent:
If Not IsNumeric(siteid) Then
dr = GetDataReader("SELECT siteid FROM nwsite WITH (NOLOCK) WHERE mac_address = '" & siteid & "'")
If Not dr.HasRows Then
Response.Write(sep & siteid & "=" & siteid)
sep = ","
End If
If Not dr Is Nothing Then
dr.Close()
End If
End If
I need help with the If statements more than anything.
Thanks
See here for the docs you need to check, also here
Seems like you also need to learn how to use mySQL in PHP
The code is relatively easy, in its simplest step- almost line for line from what you have to help you see the transformation to PHP (there are better implementations):
$siteid = 1; // where 1 is the value of siteid, PHP vars are prefixed with '$'
// you also could do
// if(!isset($siteid){
// if(!$siteid){
if(!is_numeric($siteid){
// there is no site ID, so get it from the DB
// Make a MySQL Connection
mysql_connect("localhost", "user", "password") or die(mysql_error());
mysql_select_db("dbname") or die(mysql_error());
// Run your query
$result = mysql_query("SELECT siteid FROM nwsite WITH (NOLOCK) WHERE mac_address = ".$siteid)
or die(mysql_error());
if(mysql_num_rows($result)>0){
// rows returned
//assuming only one row is returned, with your siteid value
$row = mysql_fetch_array( $result );
$siteid=$row['siteid'];
}else{
// no rows returned
// do something
}
}else{
// $siteid is already a valid value
}
AN if statement goes something like this:
if(someexpression){
$yourcode = "your things";
}
quick mockup of your code:
if(!is_numeric($siteid){
//sql call
$result = yourdb->query('SELECT');
if(!$result){
echo "error";
}
}
if(!$this->IsNumeric($siteid))
{
$dr = $this->GetDataReader("SELECT siteid FROM nwsite WITH (NOLOCK) WHERE mac_address = $siteid");
if(!$dr->hasRows())
{
echo "$sep$siteid = $siteid";
$sep = " , ";
}
if(!is_null($dr))
{
$dr->close();
}
}
Following code may help you:
if (!is_numeric($siteid)) {
$res = mysql_query("SELECT siteid FROM nwsite WHERE mac_address = " . mysql_real_escape_string($siteid));
$rows = mysql_fetch_assoc($res);
if (count($rows) == 0) {
print($sep . $siteid . "=" . $siteid);
$sep = ',';
}
}

Categories