I'm trying to create a php function to split up the data into batches as it fails when I try to insert them fairly quickly.
I'm trying to insert thousands of records of user-data into a different format in the same database, later to be exported to a seperate database. However the query fails.
Based on comments and answers below I've updated the code to the following. Still fails, though.
The code inserting values:
function insertUsers( $users ){
error_reporting(E_ALL);
ini_set('display_errors',1);
global $pdo;
//insert into database
$i = 0;
$base = 'INSERT INTO tth_user_accounts (user_login, user_pass, user_email, user_registered,
user_firstname, user_lastname ) VALUES ';
$sql = '';
var_dump($users);
while( $i < count( $users ) ){
$sql = $sql . ' ("' .
$users[$i]['user_login'] . '", "' .
$users[$i]['user_pass'] . '", "' .
$users[$i]['user_email'] . '", "' .
$users[$i]['user_registered'] . '", "' .
$users[$i]['meta_value']['first_name'] . '", "' .
$users[$i]['meta_value']['last_name'] . '")';
if (!( $i % 25 === 0 )){
$sql = $sql . ', ';
}
if ($i % 25 === 0) {
//execute $base + $query here
$sql = $base . $sql;
$query = $pdo->prepare( $sql );
echo 'check query: <br />';
print_r( $query );
if( $query->execute() ){
echo '50 users succesfully added to the database';
} else {
echo 'Query failed: ';
print_r( $pdo->errorInfo() );
echo '<br />';
}
$sql = ''; //Re-init query string
}
$i++;
}
if ( strlen( $sql ) > 0 ) { // Execute remainder, if any
//execute $base + $query here
$sql = $base . $sql;
$query = $pdo->prepare( $sql );
echo 'check query: <br />';
print_r($query);
if( $query->execute() ){
echo 'User succesfully added to the database';
} else {
echo 'Query failed: ';
print_r( $pdo->errorInfo() );
echo '<br />';
}
}
}
check query:
PDOStatement Object ( [queryString] => INSERT INTO tth_user_accounts (user_login, user_pass, user_email, user_registered, user_firstname, user_lastname ) VALUES ("John Smith", "4\/\/350M3 P4sS\/\/0r|)", "john.smith#greatmail.com", "2013-04-11 11:18:58", "John", "Smith") )
Query failed: Array ( [0] => 00000 [1] => [2] => )
Tried it with a %25 and %50, both don't work. Keep getting the 00000 error which is supposed to lead to victory (success, though for me it still fails, nothing in the DB)
I'd do it manually if I had the time but this won't be a one-time event so I need a solution to this issue. Is there a good way to split up the query into batches (and how?) that would allow this to be repeated and queries to be executed one after the other? I've been looking at a whole bunch of questions on SO (and elsewhere) already and can't find one that suits my needs.
UPDATE - has been answered, need a small modification as shown below:
if (!( $i % 25 === 0 )){
if(!( $i == ( count( $users ) - 1 ))){
$sql = $sql . ', ';
}
}
You're mixing PDO with mysql_ functions. Pick one and follow the respective library's error handling.
It would also be beneficial print out your $sql to yourself to see if it's formatted correctly. Additionally, if you're handling POSTed data, you will want to use prepared statements,
You might need to increase the max_allowed_packet value which defaults to 1Mb. If you want to split up the query in batches you can do so using the modulus operator.
$base = 'INSERT INTO ...';
$sql = '';
while( $i < count( $users ) ){
$sql = $sql . ' ("' ... //etc.
if ($i % 50 === 0) {
//execute $base + $qry here
$sql = ''; //Re-init query string
}
}
if (strlen($qry)>0) { // Execute remainder, if any
//execute $base + $query here
}
Or as an array (described here):
$base = 'INSERT INTO ...';
$sql = array();
while( $i < count( $users ) ){
$sql[] = ' ("' ... //etc.
if ($i % 50 === 0) {
//execute $base + implode(',', $sql) here
$sql = array(); //Re-init query string
}
}
if (sizeof($qry)>0) { // Execute remainder, if any
//execute $base + implode(',', $sql) here
}
Also please make sure you're using prepared statements correctly so you're not vulnerable to SQL injections.
Finally: you might need to enable error reporting so failures won't be silent; if they're still silent after enabling error reporting (e.g. error_reporting(-1);) you might need to set MySQL to strict mode (not sure if that will help). If they still fail silently file a bugreport.
Edit
Oh, I missed the fact that you're mixing mysql_ and PDO; that will probably be the reason why you're not seeing any errors... D'uh. Go read the manual on PDO error handling.
#Downvoters: If you're downvoting at least have the decency to leave a comment on why.
Is there a reason why you want to do it as one single statement? Why not just iterate through the users and insert each one with something like:
while($i < count( $users )) {
$sql = 'INSERT INTO tth_user_accounts (user_login, user_pass, user_email, user_registered, user_firstname, user_lastname ) VALUES ';
$sql = $sql + "(";
$sql = $sql + "'" . $users[$i]['user_login'] . '",';
$sql = $sql + "'" . $users[$i]['user_pass'] . '",';
$sql = $sql + "'" . $users[$i]['user_email'] . '",';
$sql = $sql + "'" . $users[$i]['user_registered'] . '",';
$sql = $sql + "'" . $users[$i]['meta_value']['first_name'] . '",';
$sql = $sql + "'" . $users[$i]['meta_value']['last_name'] . '"';
$sql = $sql + ")";
$query = $pdo->prepare( $sql );
if( $query->execute() ){
echo 'User succesfully added to the database';
} else {
die ("Query failed: " . $pdo->errorInfo());
}
}
Even better is to prepare the statement once and then bind the parameters. As pointed out by RobIII, you can introduce SQL injection vulnerabilities by building up your SQL statements as strings so instead, you could do something like:
$sql = 'INSERT INTO tth_user_accounts (user_login, user_pass, user_email, user_registered, user_firstname, user_lastname) ';
$sql += ' VALUES (:user_login, :user_pass, :user_email, :user_registered, :user_firstname, :user_lastname)';
$query = $pdo->prepare( $sql );
while ($i < count($users)) {
$query->bindParam(":user_login", $users[$i]['user_login']);
$query->bindParam(":user_pass", $users[$i]['user_pass']);
$query->bindParam(":user_email", $users[$i]['user_email']);
$query->bindParam(":user_registered", $users[$i]['user_registered']);
$query->bindParam(":user_firstname", $users[$i]['user_firstname']);
$query->bindParam(":user_lastname", $users[$i]['user_lastname']);
if( $query->execute() ){
echo 'User succesfully added to the database';
} else {
die ("Query failed: " . $pdo->errorInfo());
}
}
Another issue you may be encountering is a simple fact that the user's name / information may contain invalid characters and killing your command. Take for instance a person's last name is "O'Maley". When building your string of values and wrapping the name with hard quotes, you would get
'O'Maley'
I ran into something similar to this about 8 years ago and had to validate certain values within the data. Once you find it, you'll know how to correct each respective value, then go back to batch mode.
Also, consider someone supplying a bogus value of a bogus / bad value of "--" which indicates rest of line is a comment... or even a ";" to indicate end of statement. That is most likely what you are running into.
As RobIII responded, and I was too quick in my response, don't test one at a time, but query the data an look at it for such anomolies / bad escape posts in the data. Fix what you need to BEFORE trying the insert.
Related
I am new to PHP and hope someone can help me with this.
I currently use the below lines to retrieve a value from a db and to output it as an array with the item's ID and value which works as intended.
Now I would need to do the same for multiple items so my input ($tID) would be an array containing several IDs instead of just a single ID and I would need the query to do an OR search for each of these IDs.
I was thinking of using a foreach loop for this to append " OR " to each of the IDs but am not sure if this is the right way to go - I know the below is not working, just wanted to show my thoughts here.
Can someone help me with this and tell me how to best approach this ?
My current PHP:
$content = "";
$languageFrm = $_POST["languageFrm"];
$tID = $_POST["tID"];
$stmt = $conn->prepare("SELECT tID, " . $languageFrm . " FROM TranslationsMain WHERE tID = ? ORDER BY sortOrder, " . $languageFrm);
$stmt->bind_param("s", $tID);
$stmt->execute();
$result = $stmt->get_result();
while($arr = $result->fetch_assoc()){
$content[] = array("ID" => $arr["tID"], "translation" => $arr[$languageFrm]);
}
My thought:
foreach($tID as $ID){
$ID . " OR ";
}
Many thanks for any help,
Mike
There are two approaches, assuming $tID is an array of IDs
Using MySQL IN() clause
This will work also when $tID is not an array, but a single scalar value.
$tID = array_map('intval', (array)$tID); // prevent SQLInjection
if(!empty($tID)) {
$query .= ' WHERE tID IN(' . implode(',', $tId) . ')';
} else {
$query .= ' WHERE 0 = 1';
}
Using OR clause, as you suggested
A bit more complicated scenario.
$conds = array();
foreach($tID as $ID) {
$conds[] = 'tID = ' . intval($ID);
}
if(!empty($conds)) {
$query .= ' WHERE (' . implode(' OR ', $conds) . ')';
} else {
$query .= ' WHERE 0 = 1';
}
As per above conditions you can try with implode();
implode($tID,' OR ');
You can also use IN condition instead of OR something like this.
implode($tID,' , ');
I am new to PHP so any help on the following would be really appreciated:
I am trying to run a query with a WHERE condition, but I only want to apply the where condition if the variable has been set (via a radio button).
I basically want to be able to say - if a variable is not set, do not include it in the where condition. The complication is that I will be doing this with 3+ variables...
At its most basic - here is the code I have so far:
if(isset($_POST['category'])){
$category = $_POST['category'];}
if(isset($_POST['brand'])) {
$brand = $_POST['brand'];
}
{
$q = 'SELECT name, category, brand, price, imageurl, purchaselink FROM clothes WHERE category = "'.$_POST['category'].'" AND brand = "'.$_POST['brand'].'"';
[truncated]
Thanks very much in advance!
You can do this with an array, but first of all DO NOT BUILD QUERIES THIS WAY since you'll be vulnerable to SQL injection. Use PDO instead.
The idea is to have a list of suitable conditions:
$conds = [ 'brand', 'category', 'name' ];
$where = [ ]; // Empty array
foreach ($conds as $cond) {
if (!empty($_POST[$cond])) {
$sql = "({$cond} = ?)"; // We use PDO and bound values.
$where[$sql] = $_POST[$cond];
// In *deprecated* MySQL we would use at least
// $sql = "({$cond} = '" . mysql_real_escape_string($_POST[$cond]) . "')";
// $where[$sql] = true;
}
}
// Now we have a list of pairs:
// brand = ? => 'MyBrand',
// name = ? => 'MyName',
if (!empty($where)) {
$sql_string .= ' WHERE (';
$sql_string .= implode( ' AND ', array_keys($where) );
$sql_string .= ')';
}
// $sql_string is now SELECT ... WHERE ( (brand=?) AND (name=?) ... )
// Using the MySQL version, we would have ... WHERE ( (brand='MyBrand') AND ... ) )
// With PDO we PREPARE the query using sql_string
// http://dev.mysql.com/doc/apis-php/en/apis-php-pdo-mysql.html
// http://www.php.net/manual/en/intro.pdo.php
// We need an open PDO connection saved into $pdo
$stmt = $pdo->prepare ($sql_string);
// Then we execute the query.
// Bind the values to array_values($where).
$stmt->execute( array_values($where) );
while ($tuple = $stmt->fetch(PDO::FETCH_ASSOC)) {
...
}
A shorter way, MySQL only (since it does not distinguish between keys and values) would be
$where = [ ]; // empty array()
foreach ($conds as $cond) {
if (empty($_POST[$cond])) {
continue;
}
// THIS IS NOT SECURE. See e.g. http://johnroach.info/2011/02/17/why-mysql_real_escape_string-isnt-enough-to-stop-sql-injection-attacks/
$escaped = mysql_real_escape_string($_POST[$cond]);
$where[] = "({$cond} = '{$escaped}')";
}
$query = "SELECT ...";
if (!empty($where)) {
$query .= " WHERE (" . implode(' AND ', $where) . ")";
}
This approach has the additional advantage that you can have the 'AND' parameterized - the user can choose whether have the conditions ANDed or ORed via a radiobutton:
$and_or = ('OR' == $_POST['andor']) ? ' OR ' : ' AND ';
$query .= " WHERE (" . implode($and_or, $where) . ")";
Note that the actual value of 'andor' is NOT used -- if it is an OR, all well and good, ' OR ' is used. Anything else that might be accidentally sent in a POST by a customer, such as "--; DROP TABLE Students;" , is considered to mean ' AND '.
you have to incluide all inside if(){}
if(isset($_POST['category']) && isset($_POST['brand'])){
$q = 'SELECT name, category, brand, price, imageurl, purchaselink FROM clothes WHERE category = "'.$_POST['category'].'" AND brand = "'.$_POST['brand'].'"';
}
I use this technique to make my filters :
$q = 'SELECT name, category, brand, price, imageurl, purchaselink FROM clothes WHERE 1=1 ';
if(isset($_POST['category'])) $q .= ' AND category ="'.$_POST['category'].'"';
if(isset($_POST['brand'])) $q .= ' AND brand = "'.$_POST['brand'].'"';
// here you can add other filters. :) be happy
Below is my small code for inserting some info into AthleteID. It doesn't actually insert the information to the table though, any help is appreciated. (sorry for asking twice, but I think my first question isn't addressing whatever issue is holding me up here!)
<?php
require_once('resources/connection.php');
echo 'hello noob' . '<br />';
$query = mysql_query('SELECT LName, MyWebSiteUserID FROM tuser WHERE MyWebSiteUserID = MyWebSiteUserID');
$athleteId = strtoupper(substr($row["LName"], 0, 2)) . $row["MyWebSiteUserID"];
$update = "UPDATE `tuser` SET `AthleteID`='$athleteId' WHERE `MyWebSiteUserID` = `MyWebSiteUserID`;";
while($row = mysql_fetch_array($query)){
mysql_query( $update);
}
Where to begin..
1) Your using mysql and not mysqli. mysql is now deprecated but you could be on a PHP 4 system so keep that in mind.
2) You are building the $athleteID before you have found out what LName and SkillshowUserID is.
3) Your using a where of 1 = 1. You dont need this as it will return true for every row.
4) So...
// Execute a query
$results = mysql_query('SELECT LName, MyWebsiteID FROM tuser WHERE SkillshowUserID = SkillshowUserID');
// Loop through the result set
while($row = mysql_fetch_array($query))
{
// Generate the athleteId
$athleteId = strtoupper(substr($row["LName"], 0, 2)) . $row["MyWebsiteID"];
// Generate an sql update statement
$update = "UPDATE `tuser` SET `AthleteID`='" . $athleteId . "' " .
" WHERE LName = '" . $row['LName'] . "' " .
" AND MyWebsiteID = '" . $row['MyWebsiteID'] . "';";
// Fire off that bad boy
mysql_query($update);
}
I'm new to PHP, and I do not it's syntax and principles very well.
I have such code:
function exportFromTransbase($table_name) {
//$odbc_query = "SELECT * FROM " . $table_name. " WHERE ((CDS_CTM subrange(248 cast integer) = 1) AND (CDS_LNG_ID = 16))";
$odbc_query = "SELECT * FROM " . $table_name. "";
$data = odbc_exec($this->odbc_id, $odbc_query);
odbc_longreadlen($data, 10485760);
$oufile=fopen($table_name.".sql", 'w') or die("error writing file");
$q1='INSERT INTO `' . substr($table_name, 4) . '` VALUES';
fwrite($oufile, $q1);
while($row = odbc_fetch_array($data))
{
foreach($row as $key => $value) {
$keys[] = "`" . $key . "`";
if ($value == ""){
$value = 'NULL';
$values[] = "" . mysql_real_escape_string($value) . "";
}
else{
$values[] = "'" . mysql_real_escape_string($value) . "'";
}
//echo "\n \n ololo ".$value;
}
$mysql_query = "\n (".implode(",", $values).")," ;
fwrite($oufile, $mysql_query);
//mysql_query($mysql_query);
set_time_limit(0);
unset($keys);
unset($values);
unset($row);
}
$stat = fstat($oufile);
ftruncate($oufile, $stat['size']-1);
fseek($oufile, 0, SEEK_END);
fwrite($oufile, ";".$r);
//} while ($r < 5 );
fclose($oufile);
if ($mysql_query){
print "Ýêñïîðò äàííûõ èç òàáëèöû " . $table_name . " çàâåðøåí!";
//strtolower(substr($table_name, 4))
}
}
what and where i need to custom, so that i export all table fields except one, for example called Size, i must insert in db is this field nulls....
Also if it is easy, how to split my sql query in batches of 5000 rows? so insert (5000 rows) then insert another 5000....
But first i need to export all fields, except one...
To the best of my knowledge, it can't be possible.
You can use simply:
SELECT field1, field2, field3, field4 FROM table
See Select all columns except one in MySQL?
And if you have more fields in your table then you can use * only.
At the time of insert you can make a condition which ignores the field which you don't want in new table.
Can you use
"select * from {table} where {sth} != {condition}"
Then you can fetch all data except one. The one is excrpted follow your condition.
Meanwhile,if you want to insert by 5000, you can read 5000 lines one time and insert into.
I want to be able to switch from the current db to multiple dbs though a loop:
$query = mysql_query("SELECT * FROM `linkedin` ORDER BY id", $CON ) or die( mysql_error() );
if( mysql_num_rows( $query ) != 0 ) {
$last_update = time() / 60;
while( $rows = mysql_fetch_array( $query ) ) {
$contacts_db = "NNJN_" . $rows['email'];
// switch to the contacts db
mysql_select_db( $contacts_db, $CON );
$query = mysql_query("SELECT * FROM `linkedin` WHERE token = '" . TOKEN . "'", $CON ) or die( mysql_error() );
if( mysql_num_rows( $query ) != 0 ) {
mysql_query("UPDATE `linkedin` SET last_update = '{$last_update}' WHERE token = '" . TOKEN . "'", $CON ) or die( mysql_error() );
}else{
mysql_query("INSERT INTO `linkedin` (email, token, username, online, away, last_update) VALUES ('" . EMAIL . "', '" . TOKEN . "', '" . USERNAME . "', 'true', 'false', '$last_update')", $CON ) or die( mysql_error() );
}
}
mysql_free_result( $query );
}
// switch back to your own
mysql_select_db( USER_DB, $CON );
It does insert and update details from the other databases but it also inserts and edits data from the current users database which I dont want. Any ideas?
Never use the php mysql_select_db() fundtion - as you've discovered the code (and the coder) gets very confused very quickly.
Explicitly state the DB in the queries:
SELECT * FROM main_database.a_table....
UPDATE alternate_db.a_table SET...
REPLACE INTO third_db.a_table...
C.
You're probably have wrong database design.
one improve that i see is that you can use one query to duplicate or update
the syntax is like :
INSERT INTO mytable (field_list.....) VALUES (values_list...)
ON DUPLICATE KEY
UPDATE field1 = val1 ...
you are reassigning $query during your while loop. this will give strange results. use $query2 for the query inside the loop