PDO / mySQL How to bind multiple values to store multiple rows - php

To store 6 invoce items from a website through a form method="post", I use several input elements like this
<input name="item[]" value="6 hour service"/>
<input name="item[]" value="1 days travel"/>
....
Serverside I copy $_POST['Item'] to an array like this
item[]= $_POST['Item'];
Then I can access item[] and it looks like this
[item] => Array
(
[0] => 6 hour service
[1] => 1 days travel
[2] => ....
)
Then this SQL statement follows
try {
$obj = $this->dbcon->prepare('INSERT INTO invoice_item
( ID, item)
VALUES(:ID,:item)');
for ($i=0;$i<6;$i++) {
if (!empty($item[0][$i])) {
$obj->bindValue(':ID', $this->dbcon->lastInsertId(), PDO::PARAM_INT);
$obj->bindValue(':item', $item[0][$i], PDO::PARAM_STR);
$succ = $obj->execute();
}
}
}
catch(PDOException $e) {
echo "Error: " . $e->getMessage();
}
This works fine for a fix amount of items.
But what is the correct way to access $_POST, bind it and to store it when the amount of items are not known ?
EDIT :
How to iterate over $_POST["Item"] directly ?

Check if $i is less than the number of items rather than less than a fixed value.
for ($i=0;$i<count($item[0]);$i++) {
if (!empty($item[0][$i])) {
alternatively you could use foreach:
foreach ($item[0] as $value) {
if (!empty($value)) {

First of all, you don't need to necessarily bind the ID, because it become from the db itself. Then, you can use an unique query.
Get the ID:
$id = $this->dbcon->lastInsertId();
Filter $_POST['Item'] to discard empty values:
$items = array_values( array_filter( $_POST['Item'] ) );
Create a string with your query:
$query = substr( "INSERT INTO invoice_item (ID, item) VALUES " . str_repeat( "($id, ?),", count($items) ), 0, -1);
Assuming a $_POST['Items'] with three not-empty values and id = 3, now $query is:
INSERT INTO invoice_item (ID, item) VALUES (3, ?),(3, ?),(3, ?)
Now you can prepare the query and executing it binding all parameters at once (using an array as ->execute argument, parameters are bind as strings):
$obj = $this->dbcon->prepare( $query );
$obj->execute( $items );

Related

Using bound parameters from two select statements and use them in one insert

I'm trying to figure out how to take bound parameter values from two different select statements and insert them into a new table.
My first select gets an array without issue. My second select gets a count, as well as a number 180 divided by the count, but uses returned values from the first select in it's where clause.
This all works perfect.
Now, based on each of the 2nd select's execution I want to insert the values from each select into one new table.
I'm binding the values from each select individually, but how can I bind the values from both selects to execute my insert on them?
$selectPLC = "
SELECT
sku_id, s.frm as frm, sg.code as code, s.matrl as matrl, s.color as color, cst
FROM plc;
";
try {
$PLCcheck = $MysqlConn->prepare($selectPLC);
$detailRslt = $PLCcheck->execute();
while ($PLCRow = $PLCcheck->fetch(PDO::FETCH_ASSOC)) {
print_r($PLCRow); //This prints the first array I need
$salesValues = [
":cst" => $PLCRow["cst"],
":frm" => $PLCRow["frm"],
":matrl" => $PLCRow["matrl"],
":color" => $PLCRow["color"]
];
$checkSales = "
SELECT
count(*) as salesCount,
180/count(*) as countDIV
FROM orders
WHERE cstnoc = :cst
AND frmc = :frm
AND covr1c = :matrl
AND colr1c = :color
AND date(substr(dateField1,1,4)||'-'||substr(dateField1,5,2)||'-'||substr(dateField1,7,2) ) between current_Date - 180 DAY AND current_Date
";
try{
$salesCheck = $DB2Conn->prepare($checkSales);
$salesResult = $salesCheck->execute($salesValues);
while ($salesRow = $salesCheck->fetch(PDO::FETCH_ASSOC)) {
print_r($salesRow); //This prints the 2nd array I need
$countValues = [
":salesCount" => $salesRow["salesCount"],
":countDiv" => $salesRow["countDiv"]
];
$insertSales = "
INSERT INTO metrics (cst, frm, matrl, color, salesCount, countDIV )
VALUES (
:cst, //from first array
:frm, //from first array
:matrl, //from first array
:color, //from first array
:salesCount, //from 2nd array
:countDiv //from 2nd array
)
";
$salesInsertPrep = $DB2Conn->prepare($insertSales);
$salesInsertExec = $salesInsertPrep->execute($countValues);
}
}catch(PDOException $ex) {
echo "QUERY TWO FAILED!: " .$ex->getMessage();
}
}
}catch(PDOException $ex) {
echo "QUERY ONE FAILED!: " .$ex->getMessage();
}
When your fetching the second array set of values, rather than add them to a new array, you can add them into the array of the first values, so instead of...
$countValues = [
":salesCount" => $salesRow["salesCount"],
":countDiv" => $salesRow["countDiv"]
];
use
$salesValues[":salesCount"] = $salesRow["salesCount"];
$salesValues[":countDiv"] = $salesRow["countDiv"];
and then...
$salesInsertExec = $salesInsertPrep->execute($salesValues);
The other thing is that you can prepare your insert once outside the loop and then execute it each time in the loop, so this would look like...
$insertSales = "
INSERT INTO metrics (cst, frm, matrl, color, salesCount, countDIV )
VALUES (
:cst, //from first array
:frm, //from first array
:matrl, //from first array
:color, //from first array
:salesCount, //from 2nd array
:countDiv //from 2nd array
)
";
$salesInsertPrep = $DB2Conn->prepare($insertSales);
while ($salesRow = $salesCheck->fetch(PDO::FETCH_ASSOC)) {
print_r($salesRow); //This prints the 2nd array I need
$salesValues[":salesCount"] = $salesRow["salesCount"];
$salesValues[":countDiv"] = $salesRow["countDiv"];
$salesInsertExec = $salesInsertPrep->execute($salesValues);
}

How to get product IDs from a '^' separated list?

I have a '^' separated list of product ID numbers, and I need to get just the product ID number, and then use it to query a SQL database. The product ID numbers are stored in the $_SESSION hash. For example:
SKUS: jpn18726^gr172645^123746^17246^eu186726^...
The code I can think of is something like this:
$prodmat = $_SESSION["product"];
if(preg_match("(\d+)(^\s*\d+)*", $prodmat) {
$stmt = "select shipcode from materials where material='???'";
}
Basically, I want to extract the product ID numbers from the '^' separated list, and then use the product ID numbers to query the DB.
Just do some explosions:
$prod_list = 'SKUS: jpn18726^gr172645^123746^17246^eu186726';
$list_parts = explode(':', $prod_list); // separate the text
$prods = explode('^', trim($list_parts[1])); // trim and put the list in an array
print_r($prods);
Result:
Array
(
[0] => jpn18726
[1] => gr172645
[2] => 123746
[3] => 17246
[4] => eu186726
)
Now you can loop through the array with your query.
foreach($prods as $product) {
$sql = "SELECT foo, bar, WHERE products WHERE id = ?";
// bind the current product
// do the query
}
You should be performing just one query if possible. If you are using mysqli, you can use the following code block, though I'll recommend pdo because it is easier when dealing with a variable number of placeholders.
This code does NOTHING to validate the input data. It assumes that your SESSION data is 100% trustworthy and reliably formatted. If you need to validate, then you will want regex to do the validating. ...perhaps something like ~^SKUS: [a-z\d]+(?:\^[a-z\d]+)*$~ if your ids only contain numbers and letters.
Code:
if (!empty($_SESSION["product"])) {
// $_SESSION["product"] = 'SKUS: jpn18726^gr172645^123746^17246^eu186726';
// "SKUS: " is fixed/constant, so just remove it by known substring position/length
$params = explode('^', substr($_SESSION["product"],6)); // trim off leading substring BEFORE exploding
$count = count($params);
$csph = implode(',', array_fill(0, $count, '?')); // comma-separated placeholders
if(!$stmt = $conn->prepare("SELECT `shipcode` FROM `materials` WHERE `material` IN ($csph);")){
echo "Syntax Error # prepare: " , $conn->error; // do not echo error on public site
}else{
array_unshift($params, str_repeat('s', $count)); // prepend the type values string
$ref = []; // add references
foreach ($params as $i => $v) {
$ref[$i] = &$params[$i]; // pass by reference as required/advised by the manual
}
call_user_func_array([$stmt, 'bind_param'], $ref);
if (!$stmt->execute()) {
echo "Error # bind_param/execute: " , $stmt->error; // do not echo error on public site
} elseif (!$stmt->bind_result($shipcode)) {
echo "Error # bind_result: " , $stmt->error; // do not echo error on public site
} else {
while ($stmt->fetch()) {
// do something with $shipcode
}
$stmt->close();
}
}
} else {
echo "Missing/Invalid product data";
}
If you need to identify your shipcodes with the corresponding id, then just add the material column to the SELECT clause and the bind_result() call.
All that said, if you can confidently validate/sanitize your SESSION data, you can avoid the convolution of a prepared statement and just write your SELECT query with IN in the WHERE clause like: WHERE materials IN (' . implode("','", $params) . ').

How to pass multiple variables in foreach php

I'd like to pass multiple variables in a foreach loop to add the value from $array_sma[] into my database. But so far I can only insert the value from $short_smas, while I'd also like to insert the values from $mid_smas. I have tried nested foreach but it's multiplying the values.
$period = array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
$sma = array(6,9);
foreach ($sma as $range) {
$sum = array_sum(array_slice($period, 0, $range));
$result = array($range - 1 => $sum / $range);
for ($i = $range, $n = count($period); $i != $n; ++$i) {
$result[$i] = $result[$i - 1] + ($period[$i] - $period[$i - $range]) / $range;
}
$array_sma[] = $result;
}
list($short_smas,$mid_smas)=$array_sma;
foreach ($short_smas as $short_sma) {
$sql = "INSERT INTO sma (short_sma)
VALUES ('$short_sma') ";
if ($con->query($sql) === TRUE) {
echo "New record created successfully<br><br>";
} else {
echo "Error: " . $sql . "<br>" . $con->error;
}
}
The code in my question works fine i.e. the value from the first sub array ($short_smas) of $array_sma[] gets inserted into the column short_sma of my msql database. The problem I have is when I try to insert the second sub array $mid_smas (see list()) from $array_sma[] in my second column of my database call mid_sma.
I think this is closed to what I want to achieve but still nothing gets inserted in the DB, source: php+mysql: insert a php array into mysql
I don't have any mysql syntax error.
$array_sma[] = $result;
$sql = "INSERT INTO sma (short_sma, mid_sma) VALUES ";
foreach ($array_sma as $item) {
$sql .= "('".$item[0]."','".$item[1]."'),";
}
$sql = rtrim($sql,",");
Main problem is that $short_smas and $mid_smas have different size. Moreover they are associative arrays so either you pick unique keys from both and will allow for empty values for keys that have only one value available or you pick only keys present in both arrays. Code below provides first solution.
// first lets pick unique keys from both arrays
$uniqe_keys = array_unique(array_merge(array_keys($short_smas), array_keys($mid_smas)));
// alternatively we can only pick those present in both
// $intersect_keys = array_intersect(array_keys($short_smas),array_keys($mid_smas));
// now lets build sql in loop as Marcelo Agimóvel sugested
// firs we need base command:
$sql = "INSERT INTO sma (short_sma, mid_sma) VALUES ";
// now we add value pairs to coma separated list of values to
// insert using keys from prepared keys array
foreach ($uniqe_keys as $key) {
$mid_sma = array_key_exists($key, $mid_smas)?$mid_smas[$key]:"";
$short_sma = array_key_exists($key, $short_smas)?$short_smas[$key]:"";
// here we build coma separated list of value pairs to insert
$sql .= "('$short_sma', '$mid_sma'),";
}
$sql = rtrim($sql, ",");
// with data provided in question $sql should have string:
// INSERT INTO sma (short_sma, mid_sma) VALUES, ('3.5', ''), ('4.5', ''), ('5.5', ''), ('6.5', '5'), ('7.5', '6'), ('8.5', '7'), ('9.5', '8'), ('10.5', '9'), ('11.5', '10'), ('12.5', '11')
// now we execute just one sql command
if ($con->query($sql) === TRUE) {
echo "New records created successfully<br><br>";
} else {
echo "Error: " . $sql . "<br>" . $con->error;
}
// don't forget to close connection
Marcelo Agimóvel also suggested that instead of multiple inserts like this:
INSERT INTO tbl_name (a,b,c) VALUES (1,2,3);
its better to use single:
INSERT INTO tbl_name
(a,b,c)
VALUES
(1,2,3),
(4,5,6),
(7,8,9);
That's why I append value pairs to $sql in foreach loop and execute query outside loop.
Also its worth mentioning that instead of executing straight sql its better to use prepared statements as they are less prone to sql injection.

how to count multiple arrays returned from query,

I have this query. The where clause is an array and it works fine.
I need to count the number of separate arrays returned, is there any way to do this,
output is:
Array ( [0] => 64 ) Array ( [0] => 64 [1] => 65 ).
////////////////////////////////////////////////////
$message = explode(",", $message_arrayME);
////////////////////////////////////////////////////
$i = 0;
foreach ($message as $key => $value) {
$i++; // increment $i by one each loop pass
////////////////////////////////////////////////////
$q = mysqli_prepare($conn, ' SELECT member_id FROM members WHERE message_id=? ');
mysqli_stmt_bind_param($q, 's', $value);
mysqli_stmt_execute($q);
mysqli_stmt_store_result($q);
//$friendNumResult =mysqli_stmt_num_rows($q);
mysqli_stmt_bind_result($q, $memberId);
while($row = mysqli_stmt_fetch($q)):
$messageArrayNumResult[] = '
'.$memberId.'
';
endwhile; mysqli_stmt_close($q);
//echo $memberIdArray;
//$friendCount = count($memberIdArray);
//print_r($memberId);
$messageArray2 = implode(",",$messageArrayNumResult);
print_r( $messageArrayNumResult);
}
Fixed it:
$friendNum1 = implode(",", $messageArrayNumResult);
$friendNum2 = explode(",", $friendNum1 );
$friendCount= count($friendNum2);}
Silly me :)
Depending on how you take your data out you can count it in a few different ways... If you already have an array of users that is one users "friend list" you can just count that array... EX:
$num_friends = count($friend_array);
Also depending on how you are taking data out of your database if each friend connection is in a row, and not one row with all friends seperated by something you use to explode you can just count the number of rows. Ex:
$q = mysqli_prepare($conn, ' SELECT member_id FROM members WHERE message_id=? ');
$num_friends = $q->get_result();
Explination of the second row here.

Insert if not exists in DB and delete if not in an array

I have an array like this :
$services = array(
array("id" => "1", "desc" => "desc 1"),
array("id" => "2", "desc" => "desc 2" ),
......
);
I want to insert those services in TABLE_SERVICE .
each service is inserted if it doesnt exists in TABLE_SERVICE , and deleted it if it exists in TABLE_SERVICE but not in $services array.
I could just delete all records in TABLE_SERVICE and then insert all $services elements ,
but that can be an issue with performance because i often have large set of data in both TABLE_SERVICE and $services .
So is there an efficient way to do this ?
Thanks.
If it was me I'd iterate over $services collecting ids:
$ids = array();
foreach($services as $service)
{
$ids[] = $service['id'];
}
Then using PHP's join and the select NOT IN
"DELETE FROM TABLE_SERVICE WHERE id NOT IN (" . join($ids,',') . ")"
After that, iterate over the array again to insert/update using ON DUPLICATE KEY
"INSERT INTO TABLE_SERVICE (id,desc) VALUES (?,?) ON DUPLICATE KEY UPDATE desc = ?"
Since oracle doesn't have ON DUPLICATE KEY this stackoverflow question might help you with that last part.
My answer would be that there isn't really an efficient way to do this.
I have thought about a merge, but to be efficient, you would still be better off first inserting it in a temporary table. Then you might as well just truncate table_service and then fill it again from your $service array.
Even if Kristoffer's anwser could work, it might still be slower than a truncate insert.
This is my php method to quickly insert a lot of records:
The advantage of this is, that your insert statement will be parsed only once instead for each insert, which will improve the speed greatly. Sometimes by a factor 100 or so.
$connection = oci_connect(<YOUR CONNECTION>);
$sql = insert into table_service (id, var) values (:id, :var); // desc is a reserved word, cannot be a column name
$parsed = oci_parse($connection, $sql);
$binds = array(':id', ':var');
$sizes = array(6, 20);
$data = $services;
$errors = execute_multiple($binds, $sizes, $data);
if ($errors > 0)
// log or show
else
// feedback: full succes!
function execute_multiple($binds, $sizes, $data, $commit = true)
{
$errorCount = 0;
// first determine all binds once
foreach ($binds as $i => $bind)
{
// ${trim($bind, ':')} example: :some_id -> $some_id
oci_bind_by_name($parsed, $bind, ${trim($bind, ':')}, $sizes[$i]);
}
// Then loop over all rows and give the variables the new value for that row
// This is because the variables remain binded!
for ($row=0; $row<count($data); $row++)
{
foreach ($binds as $i => $bind)
{
$value = array_key_exists($i, $data[$row]) ? substr($data[$row][$i], 0, $sizes[$i]) : null;
${trim($bind, ':')} = trim($value);
}
if (! #oci_execute($this->parsed, OCI_DEFAULT)) // don't auto commit
$errorCount++;
}
if ($commit)
oci_commit($connection);
return $errorCount;
}

Categories