Before code :
for loop should run at least 143,792,640,000 times and create the table at least produce 563,760 rows without duplicated
I want to know how to speed up or something parallel computing like Hadoop that could accelerate between php and MySQL.
Code below:
MySQL connection
$link=mysql_connect($servername,$username,$password);
mysql_select_db($dbname);
$sql= "INSERT INTO EM (source,target) VALUES ";
for loop read data into MySQL check function if duplicate not insert and update count=count+1
for($i=0;$i<$combine_arr_size;$i++){
for($j=0;$j<$combine_arr_size;$j++){
//below check if find the duplicated like a,b we recognize b,a is same thing
if(check($combine_words_array[$i],$combine_words_array[$j])) {
$update_query="UPDATE EM SET count = count+1 where (source='$combine_words_array[$i]' AND target='$combine_words_array[$j]') OR (source='$combine_words_array[$j]' AND target='$combine_words_array[$i]');";
mysql_query($update_query);
} else {
if (!$link) {
die("Connection failed: " . mysql_error());
}
//else using insert into table () value to concatenate the string
$sql.="('$combine_words_array[$i]','$combine_words_array[$j]'),";
mysql_query(substr($sql,0,-1));
$sql= "INSERT INTO EM (source,target) VALUES ";
}
}
}
read the all vector align from comebine_word_array[] to combine_word_array[]
below is check function , check if find the pair return value
function check($src, $trg) {
$query = mysql_query("SELECT * FROM EM WHERE (source='$src' AND target='$trg') OR (source='$trg' AND target='$src');");
if (mysql_num_rows($query) > 0) {
return 1;
} else {
return 0;
}
}
table
+--------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| source | varchar(255) | YES | | NULL | |
| target | varchar(255) | YES | | NULL | |
| count | int(11) | NO | | 0 | |
| prob | double | NO | | 0 | |
+--------+--------------+------+-----+---------+-------+
now the php code just influence the source ,target and count
It is difficult to know exactly what you want to do with duplicate combinations. For example you are getting every combination of the array, which is going to get lots of duplicates which you will then count twice.
However I would be tempted to load the words into an table (possibly a temp table) and then do a cross join of the table against itself to get every combination, and use this to do an INSERT with an on duplicate key clause.
Very crudely, something like this:-
<?php
$sql = "CREATE TEMPORARY TABLE words
(
word varchar(255),
PRIMARY KEY (`word`),
)";
$link = mysql_connect($servername,$username,$password);
mysql_select_db($dbname);
$sql = "INSERT INTO words (word) VALUES ";
$sql_parm = array();
foreach($combine_words_array AS $combine_word)
{
$sql_parm[] = "('".mysql_real_escape_string($combine_word)."')";
if (count($sql_parm) > 500)
{
mysql_query($sql.implode(',', $sql_parm));
$sql_parm = array();
}
}
if (count($sql_parm) > 0)
{
mysql_query($sql.implode(',', $sql_parm));
$sql_parm = array();
}
$sql = "INSERT INTO EM(source, target)
SELECT w1.word, w2.word
FROM words w1
CROSS JOIN words w2
ON DUPLICATE KEY UPDATE `count` = `count` + 1
";
mysql_query($sql);
This does rely on having a unique key covering both the source and target columns.
But whether this is an option depends on the details of the records. For example with your current code if there were 2 words (say A and B) you would find the combination A / B and the combination B / A. But both combinations would update the same records
Put a better processor on your server and increase the RAM, then go to your php.ini settings and raise the maximum allocated memory for all the various memory/processor relative configurations.
This will empower the server further and improve the running efficiency.
If you cannot find your php.ini file. Create a new php file with the following contents and open it in the browser:
<?php phpinfo(); ?>
Make sure you delete this file after finding out where php.ini is... as an unwanted user (hacker) could find this file and it would give them detailed information leading to vulnerabilities in your server configuration.
Once you've found php.ini, do some looks online to determine settings that are not obvious and increase the memory allocations in various areas.
Related
I have a SQL - MyIsam table called links with the following structure
| field | type | null | predefined | extra |
|------------|--------------|------|------------------|----------------|
| id | int(11) | no | none | auto_increment |
| link | varcar(2083) | no | none | none |
| created_at | timestamp | no | curret_timestamp | none |
| origin | varcar(100) | no | none | none |
my insert statement is
public function createLink($user_id, $link, $origin) {
$stmt = $this->conn->prepare("INSERT INTO links(link, origin) VALUES(?,?)");
$stmt->bind_param("ss", $link, $origin);
$result = $stmt->execute();
$stmt->close();
if ($result) {
// link row created
// now assign the link to user
$new_link_id = $this->conn->insert_id;
$res = $this->createUserLink($user_id, $new_link_id);
if ($res) {
// link created successfully
return $new_link_id;
} else {
// user_link failed to create
return NULL;
}
} else {
// link failed to create
return NULL;
}
}
unfortunately sometimes i get an unexpected behavior which i'm not totally able to reproduce.
as you can see the rows with id 301 and 304 has been inserted between 286 and 293 as well as row with id 300 is between 298 and 296.
Since id is auto incremental, i would have expected to find all of them ordered from the smaller to the higher value.
This is bad for my application because i need that the chronological order of creation is respected and followed whenever i query the db to get all the links
public function getAllUserLinks($user_id) {
$stmt = $this->conn->prepare("SELECT l.* FROM links l, users_links ul WHERE l.id = ul.link_id AND ul.user_id = ?");
$stmt->bind_param("i", $user_id);
$stmt->execute();
$links = $stmt->get_result();
$stmt->close();
return $links;
}
i know i could add an ORDER BY... to my query but this would further slow down things.
Besides, new links should be added as last row of the table, not in the middle , right?!?
So the physical order you see will be down to your clustered index, if you haven't defined one then MySQL will do it for you, from the InnoDB docs here:
If the table has no PRIMARY KEY or suitable UNIQUE index, InnoDB internally generates a hidden clustered index on a synthetic column containing row ID values. The rows are ordered by the ID that InnoDB assigns to the rows in such a table. The row ID is a 6-byte field that increases monotonically as new rows are inserted. Thus, the rows ordered by the row ID are physically in insertion order.
I'm assuming you're using InnoDB but MyISAM would also use the clustered index to determine physical order. Some more info here about clustered indexes.
Edit
The comment below is correct to say clustered indexes are how data is stored and strictly speaking not how data is retrieved. In practice there's a good chance you'll find data returned in physical order if you don't specify an ORDER BY.
I was attempting to explain why the questioner might be seeing the ordering he was but I'm not saying this should be relied upon, no guarantees are given about the order of data without an ORDER BY. From the SQL 92 spec (in various places):
If an is not specified, then the ordering of the rows of Q is implementation-dependent
I am currently making an attendance website. The data for attendance is stored like this...
+-----------------------------------------------+
| Name | 12/20/16 | 12/21/16 | 12/23/16 |
+-----------------------------------------------+
|Person1 | 1 | 0 | 1 |
|Person2 | 0 | 1 | 0 |
|Person3 | 1 | 1 | 1 |
+-----------------------------------------------+
If a person was there, then the date column for their row is marked as a "1". If they weren't there, then the date column for their row is marked as a "0".
I am trying to make a readout of how many days they were present.
How can I get a sum of all the values in the date columns for that specific person's row in PHP?
EDIT: I understand that it is a bad way of formatting the data. This is per the owners request. They have their mind set on it and won't listen to reason. They are thinking of SQL as an Excel file.
Since you can't refactor the database to work the only way to do this is
SELECT name, `12/20/16`+`12/21/16`+`12/23/16` as days_attended
FROM tablename
and yes every time you add a column you have to change your query.
You could make a dynamic query -- use the above as a template as to what that dynamic query would look like.
But you REALLY should refactor the database and make a view for your user to make them happy.
This is exactly why views exist.
Okay so with the help of some people in the comments, I have put together a working function to accomplish what I needed.
$ppl = mysqli_query($conn, "SELECT * FROM Attendance2016 WHERE name = '" . getSessionVal("Name") . "'");
$row = mysqli_fetch_array($ppl);
$loopMax = count($row);
$currentAtttendance = 0;
for($x = 0; $x < $loopMax; $x++){
if($row[$x] === "0"){
continue;
}else if($row[$x] === "1"){
$currentAtttendance = $currentAtttendance + 1;
}
}
return $currentAtttendance;
I'm working on a data monitor for solar panels.
Now i'm working on limited space, and getting a lot of data which is being logged on the database.
To downsize my space i was trying to find a way to make the datatables count everything every 15 minutes, and store it into a new table, and deleting the old tables.
I tried to do this with a cronjob, and later on tried to make a php script which would be handling it.
Now that code did not come close to what i was planning to have, and i'm stuck at this problem, and i know that there are probably people who do know the answer to this question.
I came across similar problems with searching through the site, but did not come across a "Count and Delete" question.
This to limit the space it uses.
Simply said, i'm trying to find a way with php to make it count and store the data records from "inverters" to "inverters_day", and deleting the excisting records from "inverters".
The Datatables are as following:
| timestamp | timestamp | No | CURRENT_TIMESTAMP
| inverter | int(11) | No |
| wh | int(11) | No |
| dcp | int(11) | No |
| dcc | float | No |
| efficiency | float | No |
| acf | int(11) | No |
| acv | float | No |
| temp | float | No |
| status | int(11) | No |
Example of data:
|2016-01-08 08:34:24|110134878|889901|0|0.05|0|49|55|2|1
|2016-01-08 08:34:59|110134878|889901|0|0.05|0|49|55|2|1
|2016-01-08 08:35:23|110048316|643076|0|0.05|0|49|55|1|1
Inverter_day is a duplication of the one above, structure it the same.
sorry, i forgot to add the code i tried.
if ($sql = mysqli_query ($conn, "SELECT COUNT (*) FROM logs WHERE timestamp < NOW() - INTERVAL 15 MINUTES")) {
$row_slt = mysqli_num_rows($sql);
if ($row_slt > 0 ) {
$sql = mysqli_query ($conn, "INSERT INTO inverter_day (timestamp, inverter, wh, dcp, dcc, efficiency, acf, acv, temp, status) SELECT timestamp, inverter, wh, dcp, dcc, efficiency, acf, acv, temp, status FROM logs WHERE timestamp NOT IN (select timestamp from inverter_day)");
} else if ($row_slt == 0) {
echo "<br> The Tables are up to date <br>";
} else {
echo "<br> Oops something went wrong. Please try again";
}
}
As i haven't had any further help with this, i just went on and left this part behind, untill i came across the INSERT SELECT feature.
The solution to the problem was the use of:
$query = "INSERT INTO enecsys_day (id, wh, dcpower, dccurrent, efficiency, acfreq, acvolt, temp, state) SELECT id, SUM(wh), SUM(dcpower), SUM(dccurrent), SUM(efficiency), SUM(acfreq), SUM(acvolt), SUM(temp), SUM(state) FROM enecsys GROUP BY id HAVING COUNT(id) > 1";
The full code i used:
<?php
mysql_connect($servername,$username,$password);
mysql_select_db($database);
$query = "INSERT INTO enecsys_day (id, wh, dcpower, dccurrent, efficiency, acfreq, acvolt, temp, state) SELECT id, SUM(wh), SUM(dcpower), SUM(dccurrent), SUM(efficiency), SUM(acfreq), SUM(acvolt), SUM(temp), SUM(state) FROM enecsys GROUP BY id HAVING COUNT(id) > 1";
$resultaat = mysql_query($query);
while ($row = mysql_fetch_array($resultaat))
{
?>
<tr>
<td><?php $row["id"]; ?></td>
<td><?php $row["SUM(wh)"]; ?></td>
</tr>
<br />
<?php
}
$delete = "DELETE FROM enecsys";
$dresultaat = mysql_query($delete);
?>
I want to avoid rows inserted that contain numbers in a range that has already been allocated. To avoid the overlapping I heard I can do it via triggers or by writing a PHP application logic but the problem is I keep getting the trigger statement wrong. This might sound awkward but I need someone to write it out for me.
I need a MySQL trigger query or procedure that will not permit users to put in numbers in a range that already exists in a database. I am dealing with cards here and the amount of cards to be manufactured by user1 will be inserted via create page before he/she commence. While user2 can commence on another machine but without prior info abt where user1 started from. So for example if user2 attempts to choose to start anything from 1,2,3,4,5,6, up to 2530 a trigger or error should occur which prevents them from duplicating the range already done by user1
id |user_id| number_from | number_to | client | cardtype | job_number | job_quant |status
1 | smith | 1 | 2530 | queens |inspiration| j290122 | 5000 |nt done
2 | john | 2531 | 4000 | queens |inspiration| j290122 | 5000 |nt done
4 | kenny | 1 | 1500 |seabirds| love | j300011 | 1500 | done
3 | wayne | 4001 | 5000 | queens |inspiration| j290122 | 5000 | done
My code for insert in PHP which I learnt how to write using a video tutorial:
<?php
if (isset($_POST['submit'])) {
// process the form
$user_id = (int) $_POST["user_id"];
$number_from = $_POST["number_from"];
$number_to = $_POST["number_to"];
$client = mysql_prep($_POST["client"]);
$cardtype = $_POST["cardtype"];
$job_number = $_POST["job_number"];
$job_quantity = $_POST["job_quantity"];
$status = $_POST["status"];
$query = "INSERT INTO jobs (";
$query .= " user_id, number_from, number_to, client, cardtype, job_number, job_quantity, status";
$query .= ") VALUES (";
$query .= " '{$user_id}', '{$number_from}', '{$number_to}', '{$client}', '{$card_type}', '{$job_number}', '{$job_quantity}', '{$status}'";
$query .= ")";
$result = mysqli_query($connection, $query);
if ($result) {
// success
$_SESSION["message"] = "job created.";
redirect_to("manage_job.php");
} else {
// failure
$_SESSION["message"] = "job creation failed.";
redirect_to("new_job.php");
}
}
?>
I was told to try this:
Create a procedure and call it in a BEFORE trigger, passing the NEW.job_number, new.number_from and new.number_to, then querying.
Said my code would then have to be modified to try...catch for your mysql error. (don't know how to do this).
Said I could also do this procedurally (in PHP) by making a series of queries that do this:
LOCK TABLES jobs WRITE;
SELECT count(*) as countof FROM jobs
WHERE job_number = $job_number
AND (($number_from BETWEEN number_from AND number_to) OR ($number_to BETWEEN number_from AND number_to));
// Fetch the result, and if it's == 0, then you insert.
UNLOCK TABLES
To avoid the overlapping you can do it via triggers or by writing application logic (in PHP in your case). In both cases you would need as parameters new_number_from and new_number_to. The former is the number_from value of the new entry. The latter is the number_to value of the new entry. You would also need new_job_number.
The query below returns all rows that overlap with the range specified by the parameters $new_number_from and $new_number_to for job with id $job_number
SELECT *
FROM jobs
WHERE job_number=$jobNumber
AND number_from <= $new_number_to
AND number_to >= $new_number_from
If you implement this using PHP, you could execute this query and check if any row is returned. If an empty resultset is returned then there is no conflict. Otherwise there is a conflict.
NOTE: You may prefer to get the count (SELECT COUNT(*)) instead of returning lines (SELECT *)
Consider these three mysql statements:
select * from Users;
select id, title, value from Blogs;
select id, feelURL, feelTitle from Feeds where id = 1;
Now im not very good at REGEX, but i want to get the table name from the mysql query. Could someone possibly create one for me with a little explanation.
Thanks,
You can actually use MySQL as the parser and get the tablenames in your query no matter how complex your SQL syntax.
(Sorry that this is a late response to your question - I just had the same problem today and found this solution.)
Simply prefix your query with the word EXPLAIN and the results set returned to PHP will include id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra. The third column is the name of each table in your query.
For example, if your query was:
select count(*) from ey_def left join ey_rels on def_id=item_id;
Use:
explain select count(*) from ey_def left join ey_rels on def_id=item_id;
And MySQL will return this to PHP:
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | ey_def | index | NULL | PRIMARY | 4 | NULL | 87 | Using index |
| 1 | SIMPLE | ey_rels | ALL | NULL | NULL | NULL | NULL | 123 | |
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
Now you can simply process the results like any other query.
Try:
preg_match('/\bfrom\b\s*(\w+)/i',$query,$matches)
This will not work if the query has more than 1 table.
Basically the regex searchs for the complete word FROM in the query and picks the following word as the table name.
A naive implementation would be this:
preg_match("/\s+from\s+`?([a-z\d_]+)`?/i", $query, $match);
echo $query . " => " . $match[1] . "\n";
This will break when you have a subquery in your SELECT field list (and probably in a few other cases). Or when your table name contains characters beside a-z, numbers and underscores.
Parsing SQL correctly isn't trivial.
For the query string you gave, the following should do:
preg_match_all('/from (\w+)/', $query, $tables);
print_r($tables[1]);
[0] => Users
[1] => Blogs
[2] => Feeds
But like pointed out in a comment already, creating a full fledged SQL parser is a non-trivial task. Don't expect this to be usable on any and all queries you throw against it.
Wish I would have seen this earlier... Like the people above me stated, it's non-trivial parsing sql statements. To pick out the table names from a sql string, it would be a better idea to get all the table names first, then find matches in the sql (providing you don't have a million tables in your database). I just so happen to have a function on hand that does just that:
/*
Takes a sql statement and attempts to get a table name from it.
This assumes a database is already specified in the connection.
[$sql]: string; SQL statement that was executed
[$conn]: resource; MySQLi connection resource
returns table name string
*/
function get_table_names($sql,$conn){
//declare variables
$table_array = array();
$table_string = "";
//get all the table names in the selected database
$sql2 = "SHOW TABLES";
$result = mysqli_query($conn, $sql2);
//display an error if something went wrong
if (!$result) {
echo "DB Error, could not list tables\n";
echo 'MySQL Error: ' . mysqli_error($conn);
exit;
}
//fetch the rows and push the table names into $table_array
while ($row = mysqli_fetch_row($result)) {
array_push($table_array, $row[0]);
}
//loop through all the tables in the database
foreach($table_array as $table){
if(strpos($sql,$table)){ //if match is found append to string
$table_string .= " $table ";
}
}
//return a string of table name matches
return $table_string;
}
Hope that helps someone...
This should do it:
(SELECT|DELETE|UPDATE|INSERT INTO) (\*|[A-Z0-9_]+)(FROM)?([A-Z0-9_, ]+)
It will works with select delete update and insert. If you use tablename1, tablename2 it will return it as a array