I have a form where the user can insert up to five line items for an invoice. The easiest way for me to do this is to just do five inserts and do a isset check before each query. However, the problem is if I try to run the two queries one after another only the first one inserts the data. I know I can combine them into one PDO query (and that does in fact work), but it does not suit my needs. The second query does not insert.
// Connect to the database
$conn = new PDO("mysql:host=$DB_HOST;dbname=$DB_DATABASE",$DB_USER,$DB_PASSWORD);
//Set all the data here
$receiptid = $_POST['receiptid'];
// .. the rest of the POST data gets set here.
//Insert first line item
$sql = "INSERT INTO lineitems (receiptid, service, description, quantity, unitprice, linetotal)
VALUES (:receiptid, :service, :description, :quantity, :unitprice, :linetotal)";
$q = $conn->prepare($sql);
$q->execute(array(':receiptid'=>$receiptid,
':service'=>$service,
':description'=>$description,
':quantity'=>$quantity,
':unitprice'=>$unitprice,
':linetotal'=>$linetotal));
//Insert second line item
$sql = "INSERT INTO lineitems (receiptid, service2, description2, quantity2, unitprice2, linetotal2)
VALUES (:receiptid, :service2, :description2, :quantity2, :unitprice2, :linetotal2)";
$q = $conn->prepare($sql);
$q->execute(array(':receiptid'=>$receiptid,
':service2'=>$service2,
':description2'=>$description2,
':quantity2'=>$quantity2,
':unitprice2'=>$unitprice2,
':linetotal2'=>$linetotal2));
Does your table really have different columns for each entered lineitem number (i.e. service2, descriptions2, etc.)?
Perhaps you need to change the field names in your second insert to match those in the first.
If you were handling cases where you did not get expected query result properly (i.e. checking your execution results and looking at the errors if something fails, You would be able to get to the source of the problem in a hurry.)
Related
I'am using php on server side to manage data with MySQL.
I have to request a API that gives me an list of users. I need to check for each user if he is in the database.
If yes, I update his information.
If not, I insert him in the data base.
The issue is that there is more than 2000+ users each times and my code in PHP is really slow (sometimes I get 504 Gateway Time-out).
We will have even more users very soon.
How can I make my code faster ? Is Php ok ?
EDIT my codeV3 after improvement:
$userList = getFromAPI();
foreach ($userList as $userId){
$db = dbConnect();
$tagList = implode(",", $user["tagid_list"]);
$query = $db->prepare(
"INSERT INTO USERS(id, name, group) VALUES(:id, :name, :group)
ON DUPLICATE KEY UPDATE name=values(name), group=values(group)"
);
$query->execute([
"id"=>$id,
"name"=>$name,
"group"=>$group
]);
}
Maybe try with putting $db = dbConnect(); outside of your foreach?
I don't know if it is needed to open the connection in each cycle. It may be time consuming aswell.
You can use a single query for that:
INSERT INTO users (id, name)
VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Cecil')
ON DUPLICATE KEY UPDATE name = VALUES(name);
In a nutshell: you insert new rows, but if one already exists (the key is duplicated), it is updated instead. You can build your insert values in a loop so you end up with a single query instead of 4000+.
Read more here.
First of all get fetching all users ids from database out of foreach lopp and buffer it in some variable. Should be better.
I've been fighting with a bit of code for a week now, not seeing what the heck is wrong...
I have a gaming site I'm trying to build new character sheets for, the form is all done, the action pointing to another page that is strictly the sql for inserting the information into the database. We have good connection, but it is hanging at the second insert statement. The code was working previously, but we had to delete the database and rebuild it, resulting in a rebuild of the insert sql lines.
The first portion of the insert code is:
if($_POST['Submit']=="Submit")
{
$sql="INSERT INTO accounts (log_name,owner,account_type,date_joined) VALUES (\"$_POST[char_name]\",\"$_SESSION[logname]\",\"$_POST[account_type]\",NOW())";
$result = mysql_query($sql)
or die("<p>Couldn't add character.<br/>".mysql_error()." in accounts.<br/>Please send this exact message to <a href='mailto:savvannis#houston-by-night.com'>Savvannis</a> with your character's name.</p>");
echo $result;
echo $_SESSION['logname'];
$sql="INSERT INTO topdata (log_name,char_venue,sub_venue,species,char_name,create_date,gender,age,appage,nature,demeanor,concept,description,web_site,view_pword,sfa) VALUES (\"$_SESSION[logname]\",\"$_POST[char_venue]\",\"$_POST[sub_venue]\",\"$_POST[species]\",\"$_POST[char_name]\",NOW(),\"$_POST[gender]\",\"$_POST[age]\",\"$_POST[appage]\",\"$_POST[nature]\",\"$_POST[demeanor]\",\"$_POST[concept]\",\"$_POST[description]\",\"$_POST[web_site]\"\"$_POST[viewpw]\",\"$_POST[sfa]\")";
$result=mysql_query($sql)
or die ("<p>Could not create character.<br/>".mysql_error()." in topdata.<br/>Please send this exact message to <a href='mailto:savvannis#houston-by-night.com'>Savvannis</a> with your character's name.</p>");
echo $result;
When the information is entered into the form and submit is hit, I get the following:
1
Could not create character.
Column count doesn't match value count at row 1 in topdata.
Please send this exact message to Savvannis with your character's name.
I look at the database and the information is entered into the accounts table, so that statement is working, but it is hanging up on the topdata table. It's not echoing the $_SESSION['logname'] and looking at the database, it's not saving the owner, which should be $_SESSION['logname'], so I'm wondering if that statement is now somehow incorrect??
I can't figure out what the heck is wrong. Any and all help would be greatly appreciated.
You have missed a comma here: \"$_POST[web_site]\"\"$_POST[viewpw]\" in your second insert SQL.
It should be \"$_POST[web_site]\", \"$_POST[viewpw]\"
First off the error message is telling you that there is an unequal number of columns and values in your SQL
Lets have a look at that
INSERT INTO topdata (
log_name,
char_venue,
sub_venue,
species,
char_name,
create_date,
gender,
age,
appage,
nature,
demeanor,
concept,
description,
web_site,
view_pword,
sfa
) VALUES (
\"$_SESSION[logname]\",
\"$_POST[char_venue]\",
\"$_POST[sub_venue]\",
\"$_POST[species]\",
\"$_POST[char_name]\",
NOW(),
\"$_POST[gender]\",
\"$_POST[age]\",
\"$_POST[appage]\",
\"$_POST[nature]\",
\"$_POST[demeanor]\",
\"$_POST[concept]\",
\"$_POST[description]\",
\"$_POST[web_site]\"\"$_POST[viewpw]\",
\"$_POST[sfa]\"
)";
Now by formatting your SQL (which is vulnerable to sql injection) I've noticed a missing comma between web_site and viewpw values
I'm using the p4a application framework, i need to insert rows into my database via fields that i have made previously, when the user presses submit, the database should update and thus other new rows should be able to to made etc.
I'm struggling to find how to input the data into the database, i can easily do it through putting the values into the sql statement but this is completely alien to me,
The code is:
public function submit()
{
$location = $this->location->getNewValue();
$date = $this->date->getNewValue();
$merono = $this->merono->getNewValue();
$sql = $db->query("INSERT INTO 'meetingrooms'(location, date, merono)
VALUES
($location, $date, $merono)");
p4a_db::singleton()->newRow($sql, array($location));
$this->load();
location, date and merono are all set in the fields i have created before this function and it should work as i have previously done the same for a login page, so i know the first section should be getting the variables. and as i have accessed the db previously i know that it is connecting, so it must be to do with the MySQL statement.
Thanks,
Steve.
on your query, i found out that you are enclosing the table name with single quote, if you want to escape tableName or columnName use backtick instead,
INSERT INTO `meetingrooms`(location, date, merono)
VALUES ($location, $date, $merono)
but since your tableName is not a reserved word or contains any invalid characters, you can get rid of the backtick.
If you are inserting values on the table which are not numeric, wrap it with single quotes,
INSERT INTO meetingrooms (location, date, merono)
VALUES ('$location', '$date', '$merono')
I finally managed to figure it out (even though this question was only active for a few mins XD)
the SQL statement was wrong for a start (Thanks to John Woo for the help (y)) now the statement goes:
query("INSERT INTO meetingrooms(location, date, merono)
VALUES
('$location', '$date', '$merono')");
this successful statement allows for the variables placed into $location $date and $merono to be inserted into the table plus the extra addition to the start of the statement
goes as:
p4a_db::singleton()->
this calls the P4A database extension which in this pop-up class i have made, is not accessible,
so the full function now goes:-
public function submit()
{
$location = $this->AreaName->getNewValue();
$date = $this->date->getNewValue();
$merono = $this->merono->getNewValue();
p4a_db::singleton()->query("INSERT INTO meetingrooms(location, date, merono)
VALUES
('$location', '$date', '$merono')");
Thanks for the help,
Steve.
I'm working with PDO connection for mysql and I'd like to have some opinion on a query I use to check if tags are present on the database, and to add it in the case it isn't.
// the tags are allready processed in $tags array
$check_stmt = $connection->prepare ("SELECT * FROM tags WHERE tag_name = :tag_name");
$save_stmt = $connection->prepare ("INSERT INTO tags (tag_name) VALUES (:tag_name)");
foreach ($tags as $current_tag) {
$check_stmt->bindParam (':tag_name', $current_tag, PDO::PARAM_STR, 32);
$save_stmt->bindParam (':tag_name', $current_tag, PDO::PARAM_STR, 32);
$check_stmt->execute ($current_tag);
if ($check_stmt->rowCount() == 0) $save_stmt->execute ($current_tag);
}
I'm not skilled with databases so I'm not sure if the query is well projected
I'd adjust your selection query a bit, to optimize:
SELECT 1 AS found FROM tags WHERE tag_name = :tag_name LIMIT 1
SELECTing * transmits much more data (all fields in matching records) from the db to your app than is necessary. selecting only the fields you need is much more efficient, and in this case it looks like you're just checking for existence, so you don't need any record data, therefore the SELECT 1.
The LIMIT 1 limits the query results to one record, instead of all matching ones. Quicker query execution and again less data transfer.
Some dirtier MySQL-specific options include simply using REPLACE INTO (don't) or the IGNORE keyword in combination with INSERT (suggested). The INSERT IGNORE syntax will be slightly faster than executing your SELECT separately.
I often have large arrays, or large amounts of dynamic data in PHP that I need to run MySQL queries to handle.
Is there a better way to run many processes like INSERT or UPDATE without looping through the information to be INSERT-ed or UPDATE-ed?
Example (I didn't use prepared statement for brevity sake):
$myArray = array('apple','orange','grape');
foreach($myArray as $arrayFruit) {
$query = "INSERT INTO `Fruits` (`FruitName`) VALUES ('" . $arrayFruit . "')";
mysql_query($query, $connection);
}
OPTION 1
You can actually run multiple queries at once.
$queries = '';
foreach(){
$queries .= "INSERT....;"; //notice the semi colon
}
mysql_query($queries, $connection);
This would save on your processing.
OPTION 2
If your insert is that simple for the same table, you can do multiple inserts in ONE query
$fruits = "('".implode("'), ('", $fruitsArray)."')";
mysql_query("INSERT INTO Fruits (Fruit) VALUES $fruits", $connection);
The query ends up looking something like this:
$query = "INSERT INTO Fruits (Fruit)
VALUES
('Apple'),
('Pear'),
('Banana')";
This is probably the way you want to go.
If you have the mysqli class, you can iterate over the values to insert using a prepared statement.
$sth = $dbh->prepare("INSERT INTO Fruits (Fruit) VALUES (?)");
foreach($fruits as $fruit)
{
$sth->reset(); // make sure we are fresh from the previous iteration
$sth->bind_param('s', $fruit); // bind one or more variables to the query
$sth->execute(); // execute the query
}
one thing to note about your original solution over the implosion method of jerebear (which I have used before, and love) is that it is easier to read. The implosion takes more programmer brain cycles to understand, which can be more expensive than processor cycles. premature optimisation, blah, blah, blah... :)
One thing to note about jerebear's answer with multiple VALUE-blocks in one INSERT:
It can be rather dangerous for really large amounts of data, because most DBMS have an upper limit on the size of the commands they can handle. If you exceed that with too many VALUE-blocks, your insert will fail. On MySQL for example the limit is usually 1MB AFAIK.
So you should figure out what the maximum size is (ideally at runtime, might be available from the database metadata), and make sure you don't exceed it by spreading your lists of values over several INSERTs.
I was inspired by jerebear's answer to build something like his second option for one of my current projects. Because of the shear volume of records I couldn't save and do all the data at once. So I built this to do imports. You add your data, and then call a method when each record is done. After a certain, configurable, number of records the data in memory will be saved with a mass insert like jerebear's second option.
// CREATE TABLE example ( Id INT, Field1 INT, Field2 INT, Field3 INT);
$import=new DataImport($dbh, 'example', 'Id, Field1, Field2, Field3');
foreach ($whatever as $row) {
// add data in the order of your column definition
$import->addValue($Id);
$import->addValue($Field1);
$import->addValue($Field2);
$import->addValue($Field3);
$import->nextRow();
}
$import->lastRow();