I've got a portion of code that is supposed to take the data entered in a form, store it in an array and then enter it into the database. I have used var_dump on $fields and $data and they are both returning the information entered in the field (in the add_habbo function). So the problem I've got is that the MYSQL/PDO code isn't inserting this data into the database.
This is the code that I am using to insert them into the database:
$fields = '`' . implode('`, `', array_keys($habbo_data)) . '`';
$data = '\'' . implode('\', \'', $habbo_data) . '\'';
var_dump($fields);
var_dump($data);
global $con;
$query = "INSERT INTO `personnel` (:fields) VALUES (:data)";
$result = $con->prepare($query);
$result->bindParam(':fields', $fields, PDO::PARAM_STR);
$result->bindParam(':data', $data, PDO::PARAM_STR);
$result->execute();
I get the impression it has something to with the bindParam sections, possibly PDO::PARAM_STR? Thanks for your assistance!
Update:
$fields = '`' . implode('`, `', array_keys($habbo_data)) . '`';
$fields_data = ':' . implode(', :', array_keys($habbo_data));
var_dump($fields);
var_dump($fields_data);
global $con;
$query = "INSERT INTO `personnel` (`rank`, `habbo_name`, `rating`, `asts`, `promotion_date`, `transfer_rank_received`, `cnl_trainings`, `rdc_grade`,
`medals`, `branch`) VALUES ({$fields_data})";
$result = $con->prepare($query);
$result->execute($habbo_data);
$arr = $result->errorInfo();
print_r($arr);
Error:
Array ( [0] => 21S01 [1] => 1136 [2] => Column count doesn't match
value count at row 1 )
Prepared statements are not the same as copy and paste!
INSERT INTO `personnel` (:fields) VALUES (:data)
You're telling PDO/MySQL here that you want to insert exactly one piece of data (:data) into one field (:field). The value is one string containing commas, not several values separated by commas.
Furthermore you can only bind data, not structural information like field names. You will have to create a query like so:
INSERT INTO `personnel` (foo, bar, baz) VALUES (?, ?, ?)
and then bind data to the three placeholders separately.
You cannot do that:
You need to add each variable / field-name and value individually;
You can only bind values and not table- or field-names.
Table- and field-names you will have to inject directly into your sql so to prevent sql injection problems, you need to check them against a white-list before doing that.
So in your case that would be something like (rough draft):
// assuming all fields have been checked against a whitelist
// also assuming that the array keys of `$habbo_data` do not contain funny stuff like spaces, etc.
$fields = '`' . implode('`, `', array_keys($habbo_data)) . '`';
$fields_data = ':' . implode(', :', array_keys($habbo_data));
var_dump($fields);
var_dump($fields_data);
global $con;
$query = "INSERT INTO `personnel` ({$fields}) VALUES ({$fields_data})";
$result = $con->prepare($query);
$result->execute($habbo_data);
Note that I am not manually binding the variables any more but sending the associative $habbo_data array directly as a parameter to the execute method, see example #2.
Related
When I run this code it should store ben in the database but, it says Array in the first_name column and it gives the string to conversion error. How would I get rid of the error?
<?php $data = ['first_name' => 'ben'] ?>
<?php $sql = "INSERT INTO names (first_name) values (?);" ?>
<?php $statement = $pdo->prepare($sql); ?>
<?php $statement->execute([$data]); ?>
PDO has two different ways to bind parameters. The first is positional. In this case, the array you pass to execute() should be an indexed array, with values in the same order that you want them to bind to the question marks:
$sql = "INSERT INTO table (col1, col2) values (?, ?)";
$data = ['value for col1', 'value for col2'];
Note the values must be in the same order that they're going to be used:
$data = ['value for col2', 'value for col1']; // This won't work, wrong order!
The alternative (and in my opinion, superior) method is to use named parameters. Here, you need to use an associative array with a key named the same as your parameter placeholder.
$sql = "INSERT INTO table (col1, col2) values (:col1, :col2)";
$data = ['col1' => 'value for col1', 'col2' => 'value for col2'];
The order of these now does not matter because they're keyed by the array name instead of the position:
$data = ['col2' => 'value for col2', 'col1' => 'value for col1']; // Still good!
Your problem (in addition to the extra array wrap that #Sammitch pointed out) is that you have mixed these two techniques together in an incompatible way -- you're using positional parameters, but have provided an associative array. So, in your case, you either need to use positional parameters and an indexed array:
$data = ['ben'];
$sql = "INSERT INTO names (first_name) values (?);";
$statement = $pdo->prepare($sql);
$statement->execute($data);
Or named parameters and an associative array:
$data = ['first_name' => 'ben'];
$sql = "INSERT INTO names (first_name) values (:first_name);";
$statement = $pdo->prepare($sql);
$statement->execute($data);
insert into test (sometext) values ("?"),("?")
$a= array("weird' text","sdfa");
I want to insert text into the table test in column sometext using bind parameter and I do not want the execute statement in a loop. I cannot implode the array in ("?"),("?") form as the query might crash coz the text can be composed of quotes.
So is there a way to achieve this using PDO in one(1) execute statement?
I cannot implode the array in ("?"),("?") form as the query might crash coz the text can be composed of quotes.
The prepared statements are there to solve quoting/escaping problems.
This syntax is wrong1:
insert into test (sometext) values ("?"),("?")
You don't have to wrap parameters by quotes, you have to write query in this form:
INSERT INTO test (sometext) VALUES (?),(?)
Then, you can use implode() without worrying about quotes:
$a = array( "weird' text", "sdfa" );
$query = "INSERT INTO test (sometext) VALUES (" . implode( "),(", array_fill( 0, count( $a ), "?" ) ) . ")";
$stmt = $db->prepare( $query );
$stmt->execute( $a );
As alternative, you can use substr and str_repeat instead of implode:
$query = "INSERT INTO test (sometext) VALUES " . substr( str_repeat( "(?),", count( $a ) ), 0, -1 );
1 Using insert into test (sometext) values ("?"),("?") you insert in your fields literally two question marks.
$stmt = $conn->prepare("INSERT INTO test (field1, field2, field3) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $field1, $field2, $field3);
// set parameters and execute
$field1 = "test";
$field2 = "test2";
$field3 = "test#test.cc";
$stmt->execute();
I need to insert many rows ( between 150 to 300 ) into MySQL table and I want to know the better of the following approaches in terms of performance:
Approach 1 :
foreach( $persons as $person ){
$stmt = $dbLink->prepare( "INSERT INTO table SET id = :ID,
name = :name,
email = :email,
mobile = :mobile");
$stmt->execute( array( ':ID'=>$person->getID(),
':name'=>$person->getName(),
':email'=>$person->getEmail(),
':mobile'=>$person->getMobile(),
) );
}
Approach 2:
$stmt = $dbLink->prepare( "INSERT INTO table SET id = :ID,
name = :name,
email = :email,
mobile = :mobile");
$stmt->bindParam( ':ID', $person->getID(), PDO::PARAM_STR );
$stmt->bindParam( ':name', $person->getName(), PDO::PARAM_STR );
$stmt->bindParam( ':email', $person->getEmail(), PDO::PARAM_STR );
$stmt->bindParam( ':mobile', $person->getMobile(), PDO::PARAM_STR );
foreach( $persons as $person ){
$stmt->execute();
}
It is the amount of calls to the database what makes the difference. Reduce the amount of calls as much as possible.
Instead of this:
insert (a,b,c) values (d,e,f);
insert (a,b,c) values (g,h,i);
insert (a,b,c) values (j,k,l);
insert (a,b,c) values (m,n,o);
do this:
insert (a,b,c) values (d,e,f),(g,h,i),(j,k,l),(m,n,o);
Thus making in one call what you would do in 4 calls.
You can use the below code to avoid multiple SQL calls and insert the data in Single SQL call
$first_string = "INSERT INTO table (id, name, email,mobile) VALUES ";//Basic query
foreach( $persons as $person )
{
$first_string .="(".$person->getID().",".$person->getName().",".$person->getEmail().",".$person->getMobile()."),";//Prepare the values
}
$final_query_string = substr($first_string, 0,-1);// This will remove the extra , at the end of the string
$stmt = $dbLink->prepare($final_query_string);
$stmt->execute();
Now execute the final query string prepared.
This way the query is prepared as the string and you need to execute it in one go.
This will make a single SQL call
To answer to your question, this is the way you should structure your prepare / bind / execute phases:
//prepare the query only the first time
$stmt = $dbLink->prepare( "INSERT table (id, name, email, mobile)
VALUES (:ID, :name, :email, :mobile)" );
//bind params and execute for every person
foreach( $persons as $person ){
$stmt->bindValue( ':ID', $person->getID(), PDO::PARAM_STR );
$stmt->bindValue( ':name', $person->getName(), PDO::PARAM_STR );
$stmt->bindValue( ':email', $person->getEmail(), PDO::PARAM_STR );
$stmt->bindValue( ':mobile', $person->getMobile(), PDO::PARAM_STR );
$stmt->execute();
}
If you have PDO::ATTR_EMULATE_PREPARES = false, the query will be prepared by mysql only the first time.
In the first case it would be re-prepared for every loop cycle
As correctly other users are saying, remember that a better performance improvement would be to make ONLY one insert instead of many insert in a for loop
EDIT: How to use parameter bindings AND one query
To use parameters' binding and only one query a solution could be:
$placeholders = ""; //this will be filled with placeholders : ( :id_1, :name_1, :email_1, :mobile_1),( :id_2 ... )
$parameters = array(); //this will keep the parameters bindings
$i = 1;
foreach( $persons as $person )
{
//add comma if not first iteration
if ( $placeholders )
$placeholders .= ", ";
//build the placeholders string for this person
$placeholders .= "( :id_$i, :name_$i, :email_$i, :mobile_$i )";
//add parameters for this person
$parameters[":id_$i"] = $person->getID();
$parameters[":name_$i"] = $person->getName();
$parameters[":email_$i"] = $person->getEmail();
$parameters[":mobile_$i"] = $person->getMobile();
$i++;
}
//build the query
$stmt = $dbLink->prepare( "INSERT INTO table (id, name, email, mobile)
VALUES " . $placeholders );
//execute the query passing parameters
$stmt->execute( $parameters );
In the first part of the loop we build the string $placeholders with a set of placeholders for every person, in the second part of the loop we store the bindings of the values of the placeholders in the $parameters array
At the end of the loop we should have all the placeholders and parameters set, and we can execute the query passing the $parameters array to the execute method. This is an alternative way in respect to use the bindValue / bindParam methods but the result should be the same
I think this is the only way to use parameter bindings AND use only one query
//declare array of values to be passed into PDO::Statemenet::execute()
$values = array();
//prepare sql string
$sql = 'INSERT INTO students ( id, name, email, mobile ) VALUES ';
foreach( $students as $student ){
$sql .= '( ?, ?, ?, ? ),'; //concatenate placeholders with sql string
//generate array of values and merge with previous values
$values = array_merge( $values, array( $student->getID(),
$student->getName(),
$student->getEmail(),
$student->getMobile(),
)
);
}
$sql = rtrim( $sql, ',' ); //remove the trailing comma (,) and replace the sql string
$stmt = $this->dbLink->prepare( $sql );
$stmt->execute( $values );
Full credits to all who have inspired me to arrive at this solution. This seems to be terse and clear:
In particular, the answer of JM4 at PDO Prepared Inserts multiple rows in single query really helped. I also recognise Moppo on this page.
I tried for long time understand what got wrong in this code:
I have two arrays that I want to put in the DB but the array can be changed any time. So it need to work dynamically.
All I get is an empty row without any data - but as string it work fine.
If I write the output string of query instead it works, but this way not:
$fields = $values = array();
$j = 0;
while ($j < mysql_num_fields($query)) {
$namee = mysql_fetch_field($query, $j)->name;
if(isset($AutoFill[$namee])){
if($AutoFill[$namee] == '?')
$values[] = "'".mysql_real_escape_string("dopd")."'";//$_POST[$namee]
else
$values[] = "'".mysql_real_escape_string($AutoFill[$namee])."'";
$fields[] = "$namee";
}
$j++;
}
$fields = implode(",", $fields);
$values = implode(",", $values);
// not working
mysql_query("INSERT INTO ".$table_name." (".$fields.") VALUES (".$values.")");
// "INSERT INTO ".$table_name." (".$fields.") VALUES (".$values.")" => tostring working:
mysql_query("INSERT INTO _users (user_name,display_name,password,email,activation_token,last_activation_request,lost_password_request,active,title,sign_up_stamp,last_sign_in_stamp) VALUES ('dopd','dopd','dopd','dopd','dopd','1409863484','0','dopd','New Member','1409863484','0')");
This will not work because you cannot pass an array into a query.
mysql_query("INSERT INTO ".$table_name." (".$fields.") VALUES (".$values.")");
Try this instead:
mysql_query( "INSERT INTO ".$table_name." ('" . implode("','", $fields) . "') VALUES ('" . implode("','", $values) . "');" );
This will create a string out of your array that will pass into the SQL statement correctly. Do your implode within the query statement rather than above. Also, you were not wrapping the values in quotes individually, so you were getting one long string of values (ie: '1,2,3') instead of individually quoted values (ie: '1','2','3').
The solution for this was that the $query was not declered properly in the right place og scope.
The code work's great on any king and length of information from user.
thank you all - best weekend.
I'm having a little trouble with my insert statement this morning. Yes, I am using the deprecated mysql_query function. My insert statement looks as follows:
$query3 = "INSERT INTO ".$db_prefix ." offer_det
(fname, lname, 10k, 14k, 18k, 21k, 22k, 24k, 925, coins, bars)
VALUES '".$fname."', '".$lname."', '".$_10k."', '".$_14k."',
'".$_18k."', '".$_21k."', '".$_22k."', '".$_24k."',
'".$_925."', '".$coins."', '".$bars."')";
$result3 = mysql_query($query3);
My PHP form values are all the variables listed in the first part of the insert statement, 'fname', etc.
My variables are set to pull from the post and are listed as the values going into the insert.
I had to change the variables to underscore before they started, I guess PHP didn't like that.
My questions:
Are those 10k, 14k, etc, okay mysql table row names?
Is there an issue I'm missing here?
The datatype for fname and lname are varchar and for the 10k through bars are decimal (7,3).
The column name 925 must be quoted using backticks.
(`fname`, `lname`, `10k`, `14k`, `18k`, `21k`, `22k`, `24k`, `925`, `coins`, `bars`)
You may also want to consider changing the column names to something else to avoid further similar problems in the future.
You should quote the 925 column name, as per MySQL Schema Object names
So correctly:
$query3 = "insert into ".$db_prefix."offer_det (fname, lname, 10k, 14k, 18k, 21k, 22k, 24k, `925`, coins, bars)
values
('".$fname."', '".$lname."', '".$_10k."', '".$_14k."', '".$_18k."', '".$_21k."',
'".$_22k."','".$_24k."', '".$_925."', '".$coins."', '".$bars."')";
Another recommendation: you should escape the incoming strings, because SQL injection is a nasty thing to experience...
Use the QUERY as like follow..
$query3 = "insert into ".$db_prefix."offer_det (fname, lname, 10k, 14k, 18k, 21k, 22k, 24k, 925, coins, bars)
values ('$fname', '$lname', '$_10k', '$_14k', '$_18k', '$_21k', '$_22k',
'$_24k', '$_925', '$coins', '$bars')";
$query_exec=mysql_query($query3) or die(mysql_error());
And for inserting a variable you need to use single codes only..
Can I be bold and suggest a change in your implementation?
/// put your vars in an easier to use format
$insert = array(
'fname' => $fname,
'lname' => $lname,
'10k' => $_10k,
/* and so on ...*/
);
/// considering you are using mysql_query, use it's escape function
foreach ( $insert as $field => $value ) {
$insert[$field] = mysql_real_escape_string($value);
}
/// pull out the keys as fields and the values as values
$keys = array_keys($insert);
$vals = array_values($insert);
/// the following should auto backtick everything... however it should be
/// noted all the values will be treated like strings as you were doing anyway
$query = "INSERT INTO `" . $db_prefix . "offer_det` " .
"(`" . implode('`,`', $keys) . "`) " .
"VALUES ('" . implode("','", $vals ) . "')";