Weird behaviour in PDO Statement with bindParam - php

I'm having some issues with inserting rows into a table using bindParam in a prepared statement.
Here my code.
$table = 'companies';
$fields = array('name', 'address', 'phone');
$values = array('Company Name', 'Company address', '555-9999999');
$sql = 'INSERT INTO '.$table.' ('.implode(', ', $fields).') VALUES ('
.substr(str_pad('', (count($fields) * 3), '?, '), 0, -2).')';
$statement = $db->prepare($sql);
for ($i = 1; $i <= count($fields); $i++) {
$statement->bindParam($i, $$fields[$i-1]);
}
for ($i = 0; $i < count($fields); $i++) {
${$fields[$i]} = $values[$i];
}
try {
$result = $statement->execute();
$this->rowCount = $result ? $statement->rowCount() : 0;
}
catch (Exception $ex) {
$this->error = $ex;
$result = false;
}
$sql becomes a string like "INSERT INTO companies (name, address, phone) VALUES (?, ?, ?)"
After binding params to some variables and giving value to those variables, I execute the clause but nothing happens, just $result is false but no error. That is, execution of the programa does not enter the catch block.
What it wrong with the code?. Any explanation?.
Thank you for your help.

I finally got to sort this out. The problem, in my case, was that I had a DateTime which I tried to store directly in the database since I had a DateTime field.
The solution: convert the DateTime field into a String doing
$dateTimeField->format('Y-m-d H:i:s')

Related

PDO bindParam() Multiple insert foreach [duplicate]

Say, we have multiple rows to be inserted in a table:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ] //[ array of values ];
Using PDO:
$sql = "insert into `table_name` (col1, col2, col3) values (?, ?, ?)" ;
Now, how should you proceed in inserting the rows? Like this?
$stmt = $db->prepare($sql);
foreach($rows as $row){
$stmt->execute($row);
}
or, like this?
$sql = "insert into `table_name` (col1, col2, col3) values ";
$sql .= //not sure the best way to concatenate all the values, use implode?
$db->prepare($sql)->execute();
Which way would be faster and safer? What is the best way to insert multiple rows?
You have at least these two options:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ];
$sql = "insert into `table_name` (col1, col2, col3) values (?,?,?)";
$stmt = $db->prepare($sql);
foreach($rows as $row)
{
$stmt->execute($row);
}
OR:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ];
$sql = "insert into `table_name` (col1, col2, col3) values ";
$paramArray = array();
$sqlArray = array();
foreach($rows as $row)
{
$sqlArray[] = '(' . implode(',', array_fill(0, count($row), '?')) . ')';
foreach($row as $element)
{
$paramArray[] = $element;
}
}
// $sqlArray will look like: ["(?,?,?)", "(?,?,?)", ... ]
// Your $paramArray will basically be a flattened version of $rows.
$sql .= implode(',', $sqlArray);
$stmt = $db->prepare($sql);
$stmt->execute($paramArray);
As you can see the first version features a lot simpler code; however the second version does execute a batch insert. The batch insert should be faster, but I agree with #BillKarwin that the performance difference will not be noticed in the vast majority of implementations.
I would do it the first way, prepare the statement with one row of parameter placeholders, and insert one row at a time with execute.
$stmt = $db->prepare($sql);
foreach($rows as $row){
$stmt-> execute($row);
}
It's not quite as fast as doing multiple rows in a single insert, but it's close enough that you will probably never notice the difference.
And this has the advantage that it's very easy to work with the code. That's why you're using PHP anyway, for the developer efficiency, not the runtime efficiency.
If you have many rows (hundreds or thousands), and performance is a priority, you should consider using LOAD DATA INFILE.
You can also go this way:
<?php
$qmarks = '(?,?,?)'. str_repeat(',(?,?,?)', count($rows)-1);
$sql = "INSERT INTO `table`(col1,col2,col3) VALUES $qmarks";
$vals = array();
foreach($rows as $row)
$vals = array_merge($vals, $row);
$db->prepare($sql)->execute($vals);
To be honest, I don't know which one will be faster, all depends on the delay between mysql and the php server.
/* test.php */
<?php
require_once('Database.php');
$obj = new Database();
$table = "test";
$rows = array(
array(
'name' => 'balasubramani',
'status' => 1
),
array(
'name' => 'balakumar',
'status' => 1
),
array(
'name' => 'mani',
'status' => 1
)
);
var_dump($obj->insertMultiple($table,$rows));
?>
/* Database.php */
<?php
class Database
{
/* Initializing Database Information */
var $host = 'localhost';
var $user = 'root';
var $pass = '';
var $database = "database";
var $dbh;
/* Connecting Datbase */
public function __construct(){
try {
$this->dbh = new PDO('mysql:host='.$this->host.';dbname='.$this->database.'', $this->user, $this->pass);
//print "Connected Successfully";
}
catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
/* Insert Multiple Rows in a table */
public function insertMultiple($table,$rows){
$this->dbh->beginTransaction(); // also helps speed up your inserts.
$insert_values = array();
foreach($rows as $d){
$question_marks[] = '(' . $this->placeholders('?', sizeof($d)) . ')';
$insert_values = array_merge($insert_values, array_values($d));
$datafields = array_keys($d);
}
$sql = "INSERT INTO $table (" . implode(",", $datafields ) . ") VALUES " . implode(',', $question_marks);
$stmt = $this->dbh->prepare ($sql);
try {
$stmt->execute($insert_values);
} catch (PDOException $e){
echo $e->getMessage();
}
return $this->dbh->commit();
}
/* placeholders for prepared statements like (?,?,?) */
function placeholders($text, $count=0, $separator=","){
$result = array();
if($count > 0){
for($x=0; $x<$count; $x++){
$result[] = $text;
}
}
return implode($separator, $result);
}
}
?>
The above code should be good solution for Inserting Multiple Records using PDO.

Parsing bulk Mysql Insert PHP [duplicate]

Say, we have multiple rows to be inserted in a table:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ] //[ array of values ];
Using PDO:
$sql = "insert into `table_name` (col1, col2, col3) values (?, ?, ?)" ;
Now, how should you proceed in inserting the rows? Like this?
$stmt = $db->prepare($sql);
foreach($rows as $row){
$stmt->execute($row);
}
or, like this?
$sql = "insert into `table_name` (col1, col2, col3) values ";
$sql .= //not sure the best way to concatenate all the values, use implode?
$db->prepare($sql)->execute();
Which way would be faster and safer? What is the best way to insert multiple rows?
You have at least these two options:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ];
$sql = "insert into `table_name` (col1, col2, col3) values (?,?,?)";
$stmt = $db->prepare($sql);
foreach($rows as $row)
{
$stmt->execute($row);
}
OR:
$rows = [(1,2,3), (4,5,6), (7,8,9) ... ];
$sql = "insert into `table_name` (col1, col2, col3) values ";
$paramArray = array();
$sqlArray = array();
foreach($rows as $row)
{
$sqlArray[] = '(' . implode(',', array_fill(0, count($row), '?')) . ')';
foreach($row as $element)
{
$paramArray[] = $element;
}
}
// $sqlArray will look like: ["(?,?,?)", "(?,?,?)", ... ]
// Your $paramArray will basically be a flattened version of $rows.
$sql .= implode(',', $sqlArray);
$stmt = $db->prepare($sql);
$stmt->execute($paramArray);
As you can see the first version features a lot simpler code; however the second version does execute a batch insert. The batch insert should be faster, but I agree with #BillKarwin that the performance difference will not be noticed in the vast majority of implementations.
I would do it the first way, prepare the statement with one row of parameter placeholders, and insert one row at a time with execute.
$stmt = $db->prepare($sql);
foreach($rows as $row){
$stmt-> execute($row);
}
It's not quite as fast as doing multiple rows in a single insert, but it's close enough that you will probably never notice the difference.
And this has the advantage that it's very easy to work with the code. That's why you're using PHP anyway, for the developer efficiency, not the runtime efficiency.
If you have many rows (hundreds or thousands), and performance is a priority, you should consider using LOAD DATA INFILE.
You can also go this way:
<?php
$qmarks = '(?,?,?)'. str_repeat(',(?,?,?)', count($rows)-1);
$sql = "INSERT INTO `table`(col1,col2,col3) VALUES $qmarks";
$vals = array();
foreach($rows as $row)
$vals = array_merge($vals, $row);
$db->prepare($sql)->execute($vals);
To be honest, I don't know which one will be faster, all depends on the delay between mysql and the php server.
/* test.php */
<?php
require_once('Database.php');
$obj = new Database();
$table = "test";
$rows = array(
array(
'name' => 'balasubramani',
'status' => 1
),
array(
'name' => 'balakumar',
'status' => 1
),
array(
'name' => 'mani',
'status' => 1
)
);
var_dump($obj->insertMultiple($table,$rows));
?>
/* Database.php */
<?php
class Database
{
/* Initializing Database Information */
var $host = 'localhost';
var $user = 'root';
var $pass = '';
var $database = "database";
var $dbh;
/* Connecting Datbase */
public function __construct(){
try {
$this->dbh = new PDO('mysql:host='.$this->host.';dbname='.$this->database.'', $this->user, $this->pass);
//print "Connected Successfully";
}
catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
/* Insert Multiple Rows in a table */
public function insertMultiple($table,$rows){
$this->dbh->beginTransaction(); // also helps speed up your inserts.
$insert_values = array();
foreach($rows as $d){
$question_marks[] = '(' . $this->placeholders('?', sizeof($d)) . ')';
$insert_values = array_merge($insert_values, array_values($d));
$datafields = array_keys($d);
}
$sql = "INSERT INTO $table (" . implode(",", $datafields ) . ") VALUES " . implode(',', $question_marks);
$stmt = $this->dbh->prepare ($sql);
try {
$stmt->execute($insert_values);
} catch (PDOException $e){
echo $e->getMessage();
}
return $this->dbh->commit();
}
/* placeholders for prepared statements like (?,?,?) */
function placeholders($text, $count=0, $separator=","){
$result = array();
if($count > 0){
for($x=0; $x<$count; $x++){
$result[] = $text;
}
}
return implode($separator, $result);
}
}
?>
The above code should be good solution for Inserting Multiple Records using PDO.

PDO update multiple rows in a single function [duplicate]

I'm working with PHP PDO and I have the following problem:
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in /var/www/site/classes/enterprise.php on line 63
Here is my code:
public function getCompaniesByCity(City $city, $options = null) {
$database = Connection::getConnection();
if(empty($options)) {
$statement = $database->prepare("SELECT * FROM `empresas` WHERE `empresas`.`cidades_codigo` = ?");
$statement->bindValue(1, $city->getId());
}
else {
$sql = "SELECT * FROM `empresas`
INNER JOIN `prods_empresas` ON `prods_empresas`.`empresas_codigo` = `empresas`.`codigo` WHERE ";
foreach($options as $option) {
$sql .= '`prods_empresas`.`produtos_codigo` = ? OR ';
}
$sql = substr($sql, 0, -4);
$sql .= ' AND `empresas`.`cidades_codigo` = ?';
$statement = $database->prepare($sql);
echo $sql;
foreach($options as $i => $option) {
$statement->bindValue($i + 1, $option->getId());
}
$statement->bindValue(count($options), $city->getId());
}
$statement->execute();
$objects = $statement->fetchAll(PDO::FETCH_OBJ);
$companies = array();
if(!empty($objects)) {
foreach($objects as $object) {
$data = array(
'id' => $object->codigo,
'name' => $object->nome,
'link' => $object->link,
'email' => $object->email,
'details' => $object->detalhes,
'logo' => $object->logo
);
$enterprise = new Enterprise($data);
array_push($companies, $enterprise);
}
return $companies;
}
}
It looks like you're trying to build a long(?) series of 'or' comparisons: if (x=1) or (x=2) or (x=3) etc.... You may find it easier to replace it with:
$cnt = count($options);
if ($cnt > 0) {
$placeholders = str_repeat(', ?', $cnt - 1);
$sql .= 'WHERE '`prods_empresas`.`produtos_codigo` IN (?' . $placeholders . ')';
}
which, if there were 5 options, would give you
WHERE prods_empresas.produtos_condigo IN (?, ?, ?, ?, ?)
And then do the values binding with:
$pos = 1;
foreach ($options as $option) {
$statement->bindValue($pos, $option->getId());
$pos++
}
You have a mismatch between the amount of bound parameters and the amount of binds in the SQL. Double check that the amount of ? and the amount of bound parameters is the same.
Additionally, HY093 will show up if you have tried to bind a parameter that does not exist:
$stmt = "INSERT INTO table VALUES (:some_value)";
$stmt->bindValue(':someValue', $someValue, PDO::PARAM_STR);
See that :some_value does not match :someValue! The fix is:
$stmt = "INSERT INTO table VALUES (:some_value)";
$stmt->bindValue(':some_value', $someValue, PDO::PARAM_STR);
Positional parameters in SQL start at 1. You're handling this by binding to position $i+1 in your $options loop.
But then you bind the last parameter for cidades_codigo to position count($options), which overwrites the last parameter set in the $options loop.
You need to bind the last parameter to position count($options)+1.
FWIW, you don't need to bindValue() at all. It's easier to just pass an array of parameters to execute(). Here's how I'd write this function:
public function getCompaniesByCity(City $city, $options = null) {
$database = Connection::getConnection();
$sql = "SELECT * FROM `empresas` WHERE `empresas`.`cidades_codigo` = ?"
$params = array();
$params[] = $city->getId();
if ($options) {
$sql .= " AND `prods_empresas`.`produtos_codigo` IN ("
. join(",", array_fill(1, count($options), "?") . ")";
foreach ((array)$options as $option) {
$params[] = $option->getId();
}
}
$statement = $database->prepare($sql);
echo $sql;
$statement->execute($params);
. . .
Also be sure to check the return value of prepare() and execute(), it will be false if there's an error, and you need to check for that and report the error. Or else enable PDO to throw exceptions on error.
I was running into this problem due to having extra entries in the named parameter mapping array passed to PDO::Statement->execute()
$args=array (":x" => 17 );
$pdo->prepare("insert into foo (x) values (:x)");
$pdo->execute($args); // success
$args[':irrelevant']=23;
$pdo->execute($args) // throws exception with HY093
Since you have made $i+1 in the loop so count($options) would equal the last $i+1 which makes a duplicate binding.Try
foreach($options as $i => $option)
{
$statement->bindValue($i + 1, $option->getId());
}
$statement->bindValue(count($options)+1, $city->getId());

php binding dynamic number of variables for batch insert query

I have a web service where a user passes up a dynamic number of questions.
On the php side I am using explode with the ? to strip out each question. I then need to do a batch insert.
What I've done so far is as follows:
$checkInQs = explode("?", trim($_POST['checkInQs'], "?"));
$checkInSql = "INSERT INTO CheckListQs (ID, GeofenceID, type, question) VALUES ";
$checkInInsertQuery = array();
$checkInInsertData = array();
foreach($checkInQs as $q){
$checkInInsertQuery[] = "('',?, 1, ?)";
$checkInData[] = $geofenceID;
$checkInData[] = $q;
}
Based on another similar example, the following would be how to finish it off with pdo:
if (!empty($checkInInsertQuery)) {
$checkInSql .= implode(', ', $checkInInsertQuery);
$stmt = $db->prepare($checkInSql);
$stmt->execute($checkInData);
}
I'm not really sure how to bind the parameters in my case. I'm using procedural binding. I would usually bind parameters like so:
mysqli_stmt_bind_param($stmt, "is", $geofenceID, $question);
mysqli_stmt_execute($stmt);
I think the type part is as simple as:
$bindVar = '';
for ($i = 0; $i < count($checkInQs); $i++){
$bindVar .= "is";
}
But not I'm not sure how to manage passing in the rest of the data?
In the end, I chose to make use of transactions, commits and rollbacks to get my desired outcome:
mysqli_query($con, "start transaction;");
$allQueriesOK = true;
$checkInQs = explode("?", trim($_POST['checkInQs'], "?"));
$checkInSql = "INSERT INTO CheckListQuestions (ID, GeofenceID, type, question) VALUES ('',?,0,?)";
mysqli_stmt_prepare($stmt, $checkInSql);
foreach ($checkInQs as $q) {
mysqli_stmt_bind_param($stmt, "is", $geofenceID, $q);
if (!mysqli_stmt_execute($stmt)){
$allQueriesOK = false;
$message = mysqli_error($con);
break;
}
}
mysqli_stmt_close($stmt);
if ($allQueriesOK){
mysqli_query($con, "commit;");
}
else{
mysqli_rollback($con);
}

PHP PDO Insert into database function issue

So I'm creating a basic cms application to try and learn PDO a bit more in depth, but I seem to have stumbled upon an issue. I want to be able to use a function to insert data into a MySQL database, and so far I've got this code...
public function insert($table, $rows, $values) {
$data = $values;
$STH = $this->DBH->("INSERT INTO `" . $table . "` (". $rows . ") values (?, ?, ?)");
}
Do the question marks for the values represent the number of pieces of data I will be entering into the database? If so how can I know the amount of pieces of data that are going to be entered based upon the $data array? (All $values will be entered with the format 'value, value2, value3' and so on)
Hopefully I've made myself clear on what I'm asking, I'm pretty bad at explaining myself haha.. Thanks.
You could do a prepared statement with a random number of values, as long as your values are an array. Example (untested):
public function insert($table, $rows, $values) {
$params = rtrim(", ", str_repeat("?, ", count($values)));
try {
$stmt = $this->DBH->prepare("INSERT INTO $table ($rows) VALUES ($params)");
for($i = 1; $i <= count($values); $i++) {
$stmt->bindValue($i, $values[$i-1]);
}
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch(PDOException $e) {
echo "Oops... {$e->getMessage()}";
}
}
Edit Passing only two parameters ($table and $data as an associative array), you could do something like this (untested):
public function insert($table, $data) {
$keys = array_keys($data);
$values = array_values($data);
$params = rtrim(", ", str_repeat("?, ", count($values)));
try {
$stmt = $this->DBH->prepare("INSERT INTO $table (`".implode("`,`", $keys)."`) VALUES ($params)");
$stmt->execute($values);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch(PDOException $e) {
echo "Oops... {$e->getMessage()}";
}
}
As long as your $data variable is an array, you can just pass it to the execute method in the PDO::Statement after it's been prepared. When question marks are used for binding it just requires a non-associative array of values in the order that you want them bound (in the order your question marks appear in the query)
public function insert($table, $rows, $values) {
$data = $values;
$STH = $this->DBH->prepare("INSERT INTO `" . $table . "` (". $rows . ") values (?, ?, ?)");
$STH->execute($data);
}
You can also use names for binding:
public function insert($table, $rows, $values) {
$data = array(":col1" => $values[0], ":col2" => $values[1], ":col3" => $values[2]);
$STH = $this->DBH->prepare("INSERT INTO `" . $table . "` (". $rows . ") values (:col1, :col2, :col3)");
$STH->execute($data);
}

Categories