php binding dynamic number of variables for batch insert query - php

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);
}

Related

mysql insert inside foreach loop, cron not working

This code works if i run in a browser. When i run the script via a cron, it doesn't get through the array and stops halfway? Why is this?
$url_array = array("eur-gbp","eur-aud","usd-chf","eur-usd","eur-jpy","gbp-jpy","eur-cad","eur-chf","usd-cad","usd-jpy","cad-chf","cad-jpy","gbp-usd","aud-usd","gbp-chf","chf-jpy","gbp-cad","aud-cad","aud-chf","aud-jpy","aud-nzd","eur-nzd","gbp-aud","gbp-nzd","nzd-chf","nzd-usd","nzd-cad","nzd-jpy");
$option_array = array(1,2,3,4,5,6,7);
$type_array = array(1,2,3,4,5,6);
foreach($url_array as $url_type) {
//code
foreach($option_array as $option) {
//code
foreach($duration_array as $duration) {
//code
foreach($type_array as $type) {
//mysql insert
$sql = "SELECT * FROM `data_analysis` WHERE date_time='".$date."' AND type='".$url_type."' LIMIT 1";
$query = $this->db->query($sql);
$result = $query->fetch_assoc();
if($result){
$sql = "UPDATE `data_analysis` SET value='".$percentage."', price_change='".$price."', parent='1' WHERE date_time='".$date."' AND type='".$url_type."'";
} else {
$sql = "INSERT IGNORE INTO `data_analysis` (date_time,value,price_change,type,parent) VALUES ('".$date."','".$percentage."','".$price."','".$url_type."','1')";
}
}
}
}
}
This isn't the exact code as it is too long to post but similar. The code works perfectly in the browser?? running via cron it stops at gbp-jpy? Why is this?
Is there a mysql query limit?
Add a unique index on (type, date_time) to the table. Then combine your two queries into 1. Also, use a prepared statement.
$stmt = $this->db->prepare("
INSERT INTO data_analysis (date_time, value, price_change, type, parent)
VALUES (?, ?, ?, ?, '1')
ON DUPLICATE KEY UPDATE value = VALUES(value), price_change = VALUES(price_change), parent = VALUES(parent)");
$stmt->bind_param("ssss", $date, $percentage, $price, $url_type);
foreach($url_array as $url_type) {
//code
foreach($option_array as $option) {
//code
foreach($duration_array as $duration) {
//code
foreach($type_array as $type) {
//mysql insert
$stmt->execute();
}
}
}
}

Mysqli:mysqli_stmt::bind_param(): Number of variables doesn't match number of parameters in prepared statement

I am creating dynamic mysqli query with the help of #chris85. I am able to created this.
<?php
require_once 'include/db.php';
$firstname = 'Alpha';
$lastname = 'Romeo';
$query = "SELECT * FROM users";
$cond = array();
$params = array();
if (!empty($firstname)) {
$cond[] = "fname = ?";
$params[] = $firstname;
}
if (!empty($lastname)) {
$cond[] = "lname = ?";
$params[] = $lastname;
}
if (count($cond)) {
$query .= ' WHERE ' . implode(' AND ', $cond);
}
echo $query;
$stmt = $mysqli->prepare($query);
if(!empty($params)) {
foreach($params as $param) {
$stmt->bind_param("s", $param);
echo $param;
}
}
$stmt->execute();
?>
When i execute this i got this.
SELECT * FROM users WHERE fname = ? AND lname = ?
Warning: mysqli_stmt::bind_param(): Number of variables doesn't match number of parameters in prepared statement in /home/u983213557/public_html/test.php on line 32
AlphaRomeo
I am not sure why it is failing. please advise me to fix this issue.
It is failing because mysqli is not PDO and you cannot bind in a loop. Thus you have to use tricks to bind an array in mysqli. Luckily, if your PHP version is 5.6 or 7, you can use this code:
$stmt = $db->prepare($query);
$types = str_repeat('s', count($param));
$statement->bind_param($types, ...$param);
$statement->execute();
if not, then you are bound to use call_user_func()-based solution

How to make the preparation more efficient in database query using PDO?

For example, I have a couple of tables in my database, e.g., user, product, etc. Fro every table, I have at least an associated class with a couple of methods, such as addUser, updateUserName, updateUserPassword, etc. For every method, I need to prepare the SQL when using PDO, which looks like this:
$sql = "INSERT INTO `user`
(`id`,`username`,`password`,`log`)
VALUES
(:id, :username, :password, :log)";
Then I store the values in an array like this:
$array = array('id'=>$id, 'username'=>$username, 'password'=>$password, 'log'=>$log);
Then I use the PDO thing:
$pdo = new PDO($dsn, $user, $password);
$mysql = $pdo->prepare($sql);
$mysql->execute($array);
So it seems that for all different methods inside the User class, I need to do this "prepare" thing. Isn't it too tedious? Is there a more efficient way to do so, especially the part where I store the values in an array considering there exist a table with many columns in which case I would end up with a very long prepare sentence?
Since Your own is insert and update try these
//to query the database with prepared statements
public function query ($sql, $parameters = array()) {
//setting error to false to prevent interferance from previous failed queries
$this->_error = false;
//prepare SQL statement
if ($this->_query = $this->_pdo->prepare ($sql)) {
//checking to see whether any parameters were submitted along
if (count($parameters)) {
//setting the initial position for the binding values
$position = 1;
//getting the individual parameters and binding them with their respective fields
foreach ($parameters as $param) {
$this->_query->bindValue ($position, $param);
$position++;
}
}
}
//executing the sql
if ($this->_query->execute()) {
//getting the number of rows returned
$this->_count = $this->_query->rowCount();
//keeping the results returned
$this->_results = $this->_query->fetchAll (PDO::FETCH_OBJ);
} else {
$this->_error = true;
}
//returning all values of $this
return $this;
}
//to insert data into a prescribed table
public function insert ($table, $parameters = array()) {
//checking if the $fields are not empty
if (count($parameters)) {
//making the keys of the array fields
$fields = array_keys ($parameters);
//creating the to-bind-values in the form (?, ?, ...)
$values = '';
$x = 1;
foreach ($parameters as $field => $value) {
//$value is different from $values
$values .= '?';
if ($x < count($parameters)) {
$values .= ', ';
$x++;
}
}
//generating $sql
$sql = "INSERT INTO `{$table}` (`".implode ('`, `', $fields)."`) VALUES ({$values})";
//executing the sql
if (!$this->query($sql, $parameters)->error()) {
return true;
}
}
return false;
}
//to update data in a prescribed table
public function update ($table, $id = null, $parameters = array()) {
//checking that $parameters is not an empty array
if (count($parameters)) {
$set = '';
$x = 1;
foreach ($parameters as $field => $value) {
$set .= "`{$field}` = ?";
if ($x < count($parameters)) {
$set .= ', ';
$x++;
}
}
if ($id) {
//generating query
$sql = "UPDATE `{$table}` SET {$set} WHERE `id` = {$id}";
} else {
$sql = "UPDATE `{$table}` SET {$set} WHERE 1";
}
//executing the query
if (!$this->query($sql, $parameters)->error()) {
return true;
}
}
return false;
}

Weird behaviour in PDO Statement with bindParam

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')

create new if already exists PHP

I am creating a random user ID, but I would like to check if the ID already has been used (very unlikely but the chances are there), but somehow this doesn't work. When I look in the database, there is no random character string in the account_id field. Do I call the functions in a wrong way?
function genRandomString() {
$length = 40;
$characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0, strlen($characters))];
}
return $string;
}
function createID() {
$cl_id = 'h_u_'.genRandomString();
}
createID();
$sql_query="SELECT * FROM accounts WHERE account_id = :cl_id";
$statement = $conn->prepare($sql_query);
$statement->bindParam(':cl_id', $cl_id, PDO::PARAM_STR);
if ($statement->execute() && $row = $statement->fetch())
{
createID();
}
$conn->exec("INSERT INTO accounts SET
account_id='$cl_id' ,
name='$_POST[name]' ,
email='$_POST[email]' ");
$cl_id is a local variable in createID() function , you need to return your value to your global code ...
function createID() {
return $cl_id = 'h_u_'.genRandomString();
}
you need to check $id in the main code
$id = createID();
$sql_query="SELECT * FROM accounts WHERE account_id = '".$cl_id."'";
$statement = $conn->prepare($sql_query);
1 . You missed to return $c_id in createID(). Change it to:
function createID() {
return 'h_u_'.genRandomString();
}
$cl_id = createID();
2 . You could use good old uniqid() instead of your custom genRandomString().
This would lead to something simpler like:
function createID() {
return 'h_u_'.uniqid();
}
$cl_id = createID();
3 . You'll have to change the if in the database related code to a loop (have a look at my example below)
4 . Your insert query uses unverified $_POST vars. This is highly prone to SQL Injections. If your Database library supports server side prepared statements you should use them and you can feel secure because data is being kept separate from the query syntax. If you are using PHP with MySQL this is the case.
If you are not using server side prepared statements you should escape any $_POST data used in the query by using mysql_real_escape_string() or something like this. In the following example I'm assuming that you are using PHP with MySQL and thatswhy I use a prepared statement.
Taking all this in account may result in a finished script like this:
$sql_query="SELECT * FROM accounts WHERE account_id = :cl_id";
$statement = $conn->prepare($sql_query);
$maxtries = 3; // how many tries to generate a unique id?
for($i = 0; $i < $maxtries; $i++) {
$cl_id = uniqid(); // create a 'unique' id
$statement->bindParam(':cl_id', $cl_id, PDO::PARAM_STR);
if (!$statement->execute()) {
die('db error');
}
$row = $statement->fetch();
if($row) {
continue;
}
break;
}
// if a unique id couldn't get generated even
// after maxtries, then pigs can fly too :)
if($i === $maxtries) {
die('maximum number of tries reached. pigs can fly!');
}
// You should use a prepared statement for the insert to prevent from
// SQL injections as you pass $_POST vars to the query. You should further
// consider to validate email address and the name!
$name = $_POST['name'];
$email = $_POST['email'];
$insert_query = '
INSERT INTO accounts SET
account_id = :account_id,
name = :name,
email = :email';
$insert_statement = $conn->prepare($insert_query);
$insert_statement->bindParam(':account_id', $cl_id, PDO::PARAM_STR);
$insert_statement->bindParam(':name', $name, PDO::PARAM_STR);
$insert_statement->bindParam(':account_id', $email, PDO::PARAM_STR);
if (!$insert_statement->execute()) {
die('db error');
}

Categories