I add a lot a values with params ($queryArr) in INSERT statemnt in foreach
public function getClients()
{
helper::putToLog("\n----- getClients\n", true);
$managedCustomerService = $this->user->GetService('ManagedCustomerService', ADWORDS_VERSION);
$selector = new Selector();
$selector->fields = array("CustomerId", "Name", "CurrencyCode");
$graph = $managedCustomerService->get($selector);
if (isset($graph->entries)) {
$accounts = [];
foreach ($graph->entries as $account) {
$accounts[$account->customerId] = $account;
}
helper::putToLog('Clients found: '.count($accounts)."\n", true);
} else {
helper::putToLog('Clients not found'."\n", true);
return false;
}
$sth = $this->db->prepare('UPDATE `adwords_clients_google` set status = 2');
$sth->execute();
$sth = null;
$queryClients = "INSERT INTO `adwords_clients_google` (`client_foreign_id`, `status`, `client_name`, `client_currency`) VALUES";
foreach($accounts as $account) {
$queryArr[$account->customerId] = "(".$account->customerId.", 1, :".$account->customerId.", :".$account->customerId."_currencyCode)";
$nameArr[$account->customerId] = $account->name;
$currencyArr[$account->customerId."_currencyCode"] = $account->currencyCode;
}
$queryClients .= implode(',', $queryArr) . " ON DUPLICATE KEY UPDATE `status` = VALUES(`status`), `client_name` = VALUES(`client_name`) ";
$sth = $this->db->prepare($queryClients);
foreach ($nameArr as $key => $value) {
$sth->bindValue(":$key", str_replace("'", "\'", $value), PDO::PARAM_STR);
}
foreach ($currencyArr as $key => $value) {
$sth->bindValue(":$key", $value, PDO::PARAM_STR);
}
print_r($sth);
try {
if ($sth->execute()) {
helper::putToLog('ok queryCampaignArr, inserted rows: ' . $sth->rowCount());
} else {
helper::putToLog('not ok', true);
}
} catch (Exception $ex) {
helper::putToLog($sth->debugDumpParams(), true);
helper::putToLog("ERROR: ".$ex->getMessage(), true);
}
return true;
}
and there are 2 array of values I need to bind $nameArr and $currencyArr. I didn't get any errors but column client_currency is empty even though array $currencyArr contains all necessary values. What's wrong?
It looks like you haven't grasped the concept of prepared+paramterized statements yet.
You prepare them once and then execute them with varying parameters (one or) multiple times.
$sth = $this->db->prepare('
INSERT INTO
`adwords_clients_google`
(`client_foreign_id`, `status`, `client_name`, `client_currency`)
VALUES
(:id, 1, :name, :currency)
ON DUPLICATE KEY UPDATE
`status` = VALUES(`status`),
`client_name` = VALUES(`client_name`)
');
$sth->bindParam(':id', $id);
$sth->bindParam(':name', $name);
$sth->bindParam(':currency', $currency);
foreach($accounts as $account) {
$id = $account->customerId;
$name = $account->name;
$currency = $account->currencyCode;
$sth->execute();
}
if you haven't got any error message/log entries please make sure that the error handling mode of your PDO instance really is set to PDO::ERRMODE_EXCEPTION.
edit: to illustrate that, here's an sscce:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly', array(
PDO::ATTR_EMULATE_PREPARES=>false,
PDO::MYSQL_ATTR_DIRECT_QUERY=>false,
PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION
));
setup($pdo); // creating temporary table + sample data for this example
printTable("before", $pdo);
$sth = $pdo->prepare('
INSERT INTO
`soFoo`
(`client_foreign_id`, `status`, `client_name`, `client_currency`)
VALUES
(:id, 1, :name, :currency)
ON DUPLICATE KEY UPDATE
`status` = VALUES(`status`),
`client_name` = VALUES(`client_name`)
');
$sth->bindParam(':id', $id);
$sth->bindParam(':name', $name);
$sth->bindParam(':currency', $currency);
$accounts = data();
foreach($accounts as $account) {
$id = $account->customerId;
$name = $account->name;
$currency = $account->currencyCode;
$sth->execute();
}
printTable("after", $pdo);
function data() {
return array_map(function($e) { return (object)$e; }, array(
array('customerId'=>1, 'name'=>'customerA', 'currencyCode'=>'cA'),
array('customerId'=>2, 'name'=>'customerB', 'currencyCode'=>'cB'),
));
}
function printTable($cap, $pdo) {
echo $cap, "\r\n";
foreach( $pdo->query('SELECT * FROM soFoo', PDO::FETCH_ASSOC) as $r ) {
echo join(', ', $r), "\r\n";
}
}
function setup($pdo) {
$pdo->exec('
CREATE TEMPORARY TABLE soFoo (
client_foreign_id int,
`status` int,
client_name varchar(32),
client_currency varchar(32),
unique(client_foreign_id)
)
');
$pdo->exec("INSERT INTO soFoo (client_foreign_id,status,client_name,client_currency) VALUES (1, 0, 'agent smith', 'kruegerrand')");
}
prints
before
1, 0, agent smith, kruegerrand
after
1, 1, customerA, kruegerrand
2, 1, customerB, cB
Related
Code below adds data in db
$sth = $this->db->prepare('UPDATE `adwords_clients_google` set status = 2');
$sth->execute();
$sth = null;
$sth = $this->db->prepare('
INSERT INTO
`adwords_clients_google`
(`client_foreign_id`, `status`, `client_name`, `client_currency`)
VALUES
(:id, 1, :name, :currency)
ON DUPLICATE KEY UPDATE
`status` = VALUES(`status`),
`client_name` = VALUES(`client_name`),
`client_currency` = VALUES(`client_currency`)
');
$sth->bindParam(':id', $id);
$sth->bindParam(':name', $name);
$sth->bindParam(':currency', $currency);
foreach($accounts as $account) {
$id = $account->customerId;
$name = $account->name;
$currency = $account->currencyCode;
$sth->execute();
}
and I would like to add try here, something like
try {
if ($sth->execute()) {
helper::putToLog('ok queryCampaignArr, inserted rows: ' . $sth->rowCount());
} else {
helper::putToLog('not ok', true);
}
} catch (Exception $ex) {
helper::putToLog($sth->debugDumpParams(), true);
helper::putToLog("ERROR: ".$ex->getMessage(), true);
}
but i don't know should I add it for every row? How can I do that?
If you are using PDO for connecting DB then use PDOException class to handle the exception.
try {
if ($sth->execute()) {
helper::putToLog('ok queryCampaignArr, inserted rows: ' . $sth->rowCount());
} else {
helper::putToLog('not ok', true);
}
} catch (PDOException $ex) {
$Exception->getMessage(); // Error message
(int)$Exception->getCode(); // Error Code
}
This is my array of order detail, array structure will be: "ids" => "qtys":
$orderData = array();
$transactionID = 1;
$orderItems = $_POST['orders'];
//echo json_encode($orderItems);
foreach ($orderItems as $order){
$qtys = $order['qty']; //Which will be "quantity" insert into database;
$ids = $order['id']; //Which will be "itemID" insert into database;
$orderData["$ids"]=$qtys;
}
Now I am looking for solutions to push the array inside the database:
$sql = "INSERT INTO item_transaction (transactionID,itemID,quantity) VALUES ($transactionID,?,?)" ;
How should I continue?
You can use bindParam to insert it inside the loop.
something like...
$stmt = $conn->prepare("INSERT INTO item_transaction transactionID,itemID,quantity) VALUES (:transactionId,:itemId,:qty)");
$stmt->bindParam(':transactionId', $transactionID );
$stmt->bindParam(':itemId', $itemID);
$stmt->bindParam(':qty', $quantity);
foreach ($orderItems as $order){
$quantity = $order['qty'];
$itemID = $order['id'];
$stmt->execute();
}
Unfortunately, you did not provide your DDL and you did not show where some of your data comes from ($transactionID ?).
<?php
class Handler {
public $dbHostname = 'DATABASE-HOSTNAME-OR-IPADDRESS-GOES-HERE';
public $dbDatabaseName = 'MYSQL-DBNAME-GOES-HERE';
public $user = 'DATABASE_USERNAME';
public $password = 'DATABASE_PASSWORD';
//public $port = 3307;
public $message;
public function handleRequest($arg) {
$orderItems = $arg['orders'];
try {
$portChunk = ( isset($this->port) ) ? ';port=' . $this->port : null;
$dsn = "mysql:dbname={$this->dbDatabaseName};host={$this->dbHostname}{$portChunk}";
$pdo = new PDO($dsn, $this->user, $this->password);
$transactionID = 1;
$stmt = $pdo->prepare("INSERT INTO item_transaction transactionID, itemID, quantity) VALUES (?, ?, ?)");
foreach ($orderItems as $order){
$stmt->execute([$transactionID, $order['id'], $order['qty']]);
}
}
catch(PDOException $e) {
$this->log('Failed: ' . $e->getMessage());
}
}
}
// Change the following to false to test from the web.
$commandLine = true;
$handler = new Handler();
if ( $commandLine ) {
// run from command line
$handler->handleRequest(['orders' =>
['qty' => 9, 'id' => 111],
['qty' => 4, 'id' => 222],
['qty' => 11, 'id' => 333],
]);
}
else {
$handler->handleRequest($_POST);
}
Bear in mind that I have NOT tested this code at all. Run it and see what happens.
I have the following code to read a JSON and store the results in a DDBB.
The code works, but it takes more than a minute to insert just 400 records.
If I open the json, it loads pretty fast.
What I'm doing wrong?
$db = new PDO('', '', '');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if(tableExists($db, 'locations') == 1)
{
$sql=$db->prepare("DROP TABLE locations");
$sql->execute();
}
$sql ="CREATE TABLE `locations` (id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, evid INT(6) NOT NULL, place VARCHAR(150), country VARCHAR(150), reg_date TIMESTAMP)" ;
$db->exec($sql);
$json = file_get_contents('thejson.php');
$data = array();
$data = json_decode($json);
foreach ($data as $key => $object)
{
if(is_object($object))
{
$id = $object->id;
$place = $object->name;
$country = substr(strrchr($object->name, "-"), 2);
$stmt = $db->prepare("INSERT INTO `locations` (evid, place, country) VALUES (:evid, :places, :country)");
$stmt->bindValue(':evid', $id, PDO::PARAM_INT);
$stmt->bindValue(':places', $place, PDO::PARAM_STR);
$stmt->bindValue(':country', $country, PDO::PARAM_STR);
$stmt->execute();
}
}
So first 2 things i would try would be moving the prepare outside the loop, and wrapping it in a transaction:
try {
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO `locations` (evid, place, country) VALUES (:evid, :places, :country)");
foreach ($data as $key => $object)
{
if(is_object($object))
{
$id = $object->id;
$place = $object->name;
$country = substr(strrchr($object->name, "-"), 2);
$stmt->bindValue(':evid', $id, PDO::PARAM_INT);
$stmt->bindValue(':places', $place, PDO::PARAM_STR);
$stmt->bindValue(':country', $country, PDO::PARAM_STR);
$stmt->execute();
}
}
$db->commit();
} catch (Exception $e) {
$db->rollBack();
throw $e;
}
Another thing you could do is try using bindParam to bind the vars by reference - this way you only need to call bindParam once on each variable name at the start, then just overwrite the values of those variables on each iteration and call execute.
try {
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO `locations` (evid, place, country) VALUES (:evid, :places, :country)");
$id = 0;
$place = '';
$country = '';
$stmt->bindParam(':evid', $id, PDO::PARAM_INT);
$stmt->bindParam(':places', $place, PDO::PARAM_STR);
$stmt->bindParam(':country', $country, PDO::PARAM_STR);
foreach ($data as $key => $object)
{
if(is_object($object))
{
$id = $object->id;
$place = $object->name;
$country = substr(strrchr($object->name, "-"), 2);
$stmt->execute();
}
}
$db->commit();
} catch (Exception $e) {
$db->rollBack();
throw $e;
}
Similar to this instead of calling bind* you could just pass the values in via execute:
try {
$db->beginTransaction();
$stmt = $db->prepare("INSERT INTO `locations` (evid, place, country) VALUES (:evid, :places, :country)");
foreach ($data as $key => $object)
{
if(is_object($object))
{
$params = array(
':id' => $object->id,
':places' => $object->name,
':country' => substr(strrchr($object->name, "-"), 2)
);
$stmt->execute($params);
}
}
$db->commit();
} catch (Exception $e) {
$db->rollBack();
throw $e;
}
I suspect using a transaction will get you a performance gain, but I dont know that there will be a ton of difference between switching up the binding methods.
Your best bet is probably to insert all the records in a single query as #PavanJiwnani suggests:
// first we need to compile a structure of only items
// we will insert with the values properly transformed
$insertData = array_map(function ($object) {
if (is_object($object)) {
return array(
$object->id,
$object->name,
substr(strrchr($object->name, "-"), 2)
);
} else {
return false;
}
}, $data);
// filter out the FALSE values
$insertData = array_filter($insertData);
// get the number of records we have to insert
$nbRecords = count($insertData);
// $records is an array containing a (?,?,?)
// for each item we want to insert
$records = array_fill(0, $nbRecords, '(?,?,?)');
// now now use sprintf and implode to generate the SQL like:
// INSERT INTO `locations` (evid, place, country) VALUES (?,?,?),(?,?,?),(?,?,?),(?,?,?)
$sql = sprintf(
'INSERT INTO `locations` (evid, place, country) VALUES %s',
implode(',', $records)
);
$stmt = $db->prepare($sql);
// Now we need to flatten our array of insert values as that is what
// will be expected by execute()
$params = array();
foreach ($insertData as $datum) {
$params = array_merge($params, $datum);
}
// and finally we attempt to execute
$stmt->execute($params);
Try to echo timestamps with milliseconds to see what is running slow. Probably executing 400 insert queries (including opening/closing connections).
There are many factors that affects the performance of the database, please provide detailed information about the database system, PHP version and related hardware.
The bottleneck could be at:
file_get_contents('thejson.php')
if the JSON contents are fetched from a remote host, i.e. DB is running normally, network is slow.
You may also want to consider move:
$stmt = $db->prepare("INSERT INTO `locations` (evid, place, country) VALUES (:evid, :places, :country)");
out of the foreach loop.
I'm having an issue with a piece of my code. It works perfectly fine, except it won't save itself to the database. This is the code:
function createOrder($user, $cart, $price, $method) {
try {
$username = "root";
$password = "";
$connection = new PDO('mysql:host=localhost;dbname=dreamlineslaapsystemen', $username, $password);
$connection->beginTransaction();
$productList=$_SESSION['products'];
$orderList=$_SESSION['orders'];
$orderItems=$_SESSION['orderitems'];
$orderid = generateOrderid();
$allOrders = array();
for($i=0; $i<count($orderList); $i++) {
array_push($allOrders, $orderList[$i]->getID());
}
while(in_array($orderid, $allOrders)) {
$orderid = generateOrderid();
}
$today = date("Y-m-d H:i:s");
$order = new Order($orderid, $user->getID(), $price, $today, $method);
$newOrder = array(
':id' => $orderid,
':userid' => $user->getID(),
':date' => $today,
':method' => $method
);
$addOrder = $connection->prepare('INSERT INTO orders(id, userid, date) VALUES (:id, :userid, :date, :method');
$addOrder->execute($newOrder);
array_push($orderList, $order);
foreach($cart->getCart() as $item => $amount) {
$itemid=null;
for($i=0; $i<count($productList);$i++) {
if($productList[$i]->getID()==$item) {
$orderitem = new Orderitem($orderid, $i, $amount);
array_push($orderItems, $orderitem);
$newOrderitem = array(
':orderid' => $orderid,
':productid' => $i,
':amount' => $amount
);
$addOrderitem = $connection->prepare('INSERT INTO orderitems(orderid, productid, amount) VALUES (:orderid, :productid, :amount');
$addOrderitem->execute($newOrderitem);
}
}
}
$connection->commit();
$_SESSION['orders']=$orderList;
$_SESSION['orderitems']=$orderItems;
return $orderid;
} catch(PDOException $e) {
$connection->rollback();
print "Er is iets fout gegaan: " . $e->getMessage() . "<br>";
return null;
}
}
It does add everything to the arrays and sessions and when I do var_dump to see if it is all stored correctly in the sessions/arrays. It just won't add to the database.
You have 3 columns yet you are inserting 4 values. I assume you have a method column in your table and your insert statements lacks closing ) parenthesis.
$addOrderitem = $connection->prepare('INSERT INTO orderitems(orderid, productid, amount, method) VALUES (:orderid, :productid, :amount, :method'));
$addOrderitem = $connection->prepare('INSERT INTO orderitems(orderid, productid, amount) VALUES (:orderid, :productid, :amount'));
I am trying to use staticsanĀ“s answer in this question for prepared statements.
Lets take this example:
$stmt = $mysqli->prepare("INSERT INTO something (userid, time, title) VALUES (?, ?, ?)");
$stmt->bind_param('iis', $userid, time(), $title);
$stmt->execute();
In staticsanĀ“s answer imploding the array is adding all the values into the mysql statement so that in the end we can insert multiple data into the database with just one statement.
How would this be done in my example?
This is completely valid:
$stmt = $mysqli->prepare("INSERT INTO something (userid, time, title) VALUES (?, ?, ?)");
$stmt->bind_param('iis', $userid, time(), $title);
$stmt->execute();
$stmt->bind_param('iis', $userid, time(), $title);
$stmt->execute();
$stmt->bind_param('iis', $userid, time(), $title);
$stmt->execute();
$stmt->bind_param('iis', $userid, time(), $title);
$stmt->execute();
You can foreach over your array of values to insert and bind and execute each time. It wont be quite as fast as the bulk insert in the example you linked, but it will be more secure.
You can build prepared statement using code as mentioned here,
PDO Prepared Inserts multiple rows in single query
PHP logic will be sort of like,
/**
* Insert With Ignore duplicates in Mysql DB.
*/
public static function insertWithIgnore($em, $container, $tableName, $fields, $rows)
{
$query = "INSERT IGNORE INTO $tableName (`" . implode('`,`', $fields) . "`) VALUES ";
$placeHolr = array_fill(0, count($fields), "?");
$qPart = array_fill(0, count($rows), "(" . implode(',', $placeHolr) . ")");
$query .= implode(",", $qPart);
$pdo = self::getPDOFromEm($em, $container);
$stmt = $pdo->prepare($query);
$i = 1;
foreach ($rows as $row) {
$row['created_at'] = date("Y-m-d H:i:s");
foreach ($fields as $f) {
if (!isset($row[$f])) {
$row[$f] = null;
}
$stmt->bindValue($i++, $row[$f]);
}
}
$result = $stmt->execute();
if ($result == false) {
$str = print_r($stmt->errorInfo(), true);
throw new \Exception($str);
}
$stmt->closeCursor();
$pdo = null;
}
/**
* Replace old rows in Mysql DB.
*/
public static function replace($em, $container, $tableName, $fields, $rows, $extraFieldValues = null)
{
if ($extraFieldValues != null) {
$fields = array_unique(array_merge($fields, array_keys($extraFieldValues)));
}
$query = "REPLACE INTO $tableName (`" . implode('`,`', $fields) . "`) VALUES ";
$placeHolr = array_fill(0, count($fields), "?");
$qPart = array_fill(0, count($rows), "(" . implode(',', $placeHolr) . ")");
$query .= implode(",", $qPart);
$pdo = self::getPDOFromEm($em, $container);
$stmt = $pdo->prepare($query);
$i = 1;
foreach ($rows as $row) {
if ($extraFieldValues != null) {
$row = array_merge($row, $extraFieldValues);
}
foreach ($fields as $f) {
$stmt->bindValue($i++, $row[$f]);
}
}
$stmt->execute();
if (!$stmt) {
throw new \Exception("PDO::errorInfo():" . print_r($stmt->errorInfo(), true));
}
$stmt->closeCursor();
$pdo = null;
}