I have read lots of forum threads and unable to solve my problem.
I'm parsing an XML file in a foreach loop and then want the parsed content to be inserted in a database table.
All is working fine but I have a problem while Inserting in the database.
Here's the code to make it "understandable" :
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
foreach ($articles->article as $article) {
$article_title = $article->title;
$article_alias = $article->alias;
...
$all_articles .= "('DEFAULT','$article_title','$article_alias'),";
}
$all_articles = rtrim($all_articles, ',');
$query = "INSERT INTO my_table (id,title, alias,) VALUES $all_articles";
My ID field is UNIQUE and Auto Increment.
What I want is to check if the alias field already exists, than update it or create new row.
I already checked ON DUPLICATE KEY UPDATE and REPLACE, but it doesn't fit my needs since it performs a duplicate check only on unique keys, or my alias field isn't and I can't change it.
Last thing I'm testing right now is to make a SELECT before INSERT INTO with a WHERE condition to look at my alias field.
I want to ask you a question here, and, if you can answer it, then you'll find the answer by yourself of your own question: What will happen if:
$article_title or $article_alias contain, hm, let's say something like "YMas's lazy programming skills"?
Let me help you - an SQL syntax error, because on this line:
$all_articles .= "('DEFAULT','$article_title','$article_alias'),";
you do not properly escape variables. Bingo, MySQL insert failed.
Rule number 1: ALWAYS, always properly handle your input data before INSERT/UPDATE/DELETE - i.e. either by using PDO and prepared statements (best), or by old-and-flawed mysqli_real_escape_string() function.
I solved it by using another joined table to be able to use ON DUPLICTAE KEY ENTRY on my alias field.
Thanks for those how helped !
Related
This question has kinda been asked already but I couldn't find my answer. I searched a while and found these related questions, but they didn't help me to understand or answer my problem.
SQL Insert Into with Inner Join
T-SQL INSERT INTO with LEFT JOIN
My question is how to insert data in 2 tables using joins. For example (with php) a user can enter his/her name and the foods he/she likes.
I store them in a variable and an array (the length of the array is not always 3 like below):
$name = "Niels"
$foodsHeLikes = array("apple", "pear", "banana");
This is how I want to store them:
USERS:
UserID name
1 Niels
FOODS:
FoodID userID name //userID is linked to UserID in users table
1 1 apple
2 1 pear
3 1 banana
The link to the first question I pasted above has an insert with a join but I don't see anywhere to put the values in like with a normal insert?
The query from that question:
INSERT INTO orders (userid, timestamp)
SELECT o.userid, o.timestamp FROM users u INNER JOIN orders o ON o.userid = u.id
Judging by what's been going on in the comment section, what you're asking is that you would like to have a more optimal query process. Right now you are using two different queries to populate your two tables, and you're wondering whether that could be done more optimally.
First things first, it's not possible to populate TWO different tables with ONE query.
However, what you could do, is use transactions.
The rest of this answer will follow the assumption that you are using PHP as your backend scripting language (as you tagged yourself).
Also, it is not inherently obvious whether you use prepared statements for your queries or not. In the case you don't, I would highly recommend using prepared statements. Otherwise, you're opening yourself up to SQL Injections (SQLI Attacks).
I will proceed by using mysqli prepared statements in this answer.
<?php
// Your input post variables
$name = $_POST['name'];
$foodArray = $_POST['foodArray'];
/*
I'm using a function to handle my queries,
simply because it makes large piles of code easier to read.
I now know that every time the function:
createUserAndFood($name, $foodArray);
is called, that it will populate my user and food table.
That way I don't have to worry about writing all the code multiple times.
*/
function createUserAndFood($name, $foodArray){
// food array values
$foodValues = array_values($foodArray);
// DB variables
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if($conn->connect_error){
die("Connection failed: " . $conn->connect_error);
}
/*
Stops the query from auto commiting,
I'll explain later, you can "maybe" disregard this.
*/
$conn->autocommit(FALSE);
// Declare the query
$sql = "INSERT INTO userTable(name) VALUES(?)";
// Prepare and bind
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $name);
// Execute the query
$stmt->execute();
// Fetch last inserted id
$lastID = $conn->insert_id;
$sql = "INSERT INTO foodTable(userId, food) VALUES(?, ?)";
$stmt = $conn->prepare($sql);
for($i = 0; $length = count($foodValues) > $i; $i++){
$stmt->bind_param("is", $lastID, $food);
$food = $foodValues[$i];
$stmt->execute();
}
// Commits the query / queries
$conn->commit();
// Close connection
$stmt->close();
$conn->close();
}
?>
Since you wanted to optimize your queries, the general idea that we are using here, is that we are making use of the MySQL function LAST_INSERT_ID(); via PHP and store it into a variable.
Now, this is mainly relevant if you are using auto incremented id's. If you are not, you can disregard this specific logic and use something else. But if you are, then keep reading.
The reason why we are storing the last id into a variable is because we need to use it multiple times (the new user might have more than one favorite food afterall). If you were not to store the last id into a variable, it would instead take the auto incremented value of the second table after the initial insert, which means upon your third insert statement and forward, you would be working with the wrong id.
Now, as I promised to explain, the reason I'm using $conn->autocommit(FALSE); and $conn->commit(); is because you might not want incomplete data sets in your database. Imagine that a user input is happening, but your database crashes in the middle of it all. You'll have incomplete data sets. If this is not really a concern of yours, then you can disregard that.
To simplify what's going on at the MySQL side of things, think of it like this:
BEGIN;
INSERT userTable SET name = '$name';
SET #lastID = LAST_INSERT_ID();
INSERT foodTable SET id = #lastID, food = '$food';
COMMIT;
First time posting to StackOverflow, but I've been a long time reader of the comments here.
I ran across a programming bug that I could not figure out after several hours, and couldn't find anything in the questions and responses that seemed to apply, so here goes my first post!
I'm using PHP and a MySQLi prepared statement to insert data that originates from a file into a database and stores each record from the file into an array ($dbData[]) that is passed to a function that performs the database storage. I have checked the array values for all four indices for each record and data exists in all record fields, although there is a lot of slashes (forward and backwards), apostrophes, quotes (single and double) and html tags in some of the string content.
The $dbData array correlates to the columns in the table for the insert.
The table is called "content" and it has a constraint on the second column that it cannot be null. It is named 'CText'.
The code that operates on the data array is in a function that is called within a loop for all of the file data. I am providing the contents of the function below without the function interface. For simplification purposes I have included code that connects to the database, but the code that actually creates the database connection resides outside the function.
$mysqli = new mysqli("example.com", "user", "password", "database");
...
$queryText = "insert into content values (?, ?, ?, ?)";
$query = mysqli->prepare($queryText);
$query->bind_param('dsds',$dbData[0],$dbData[1],$dbData[2],$dbData[3]);
if (!$query->execute()) {
echo '<br>Execute failed: (' . $query->errno . ') . $query->error;
echo '<br>dbData[1]: ' . $dbData[1];
}
The insert works for most of the $dbData record data, but I get this error on a few of the records:
Execute failed: (1048) Column 'CText' cannot be null
CText: {data printed here that is truncated after what appears to be a line return in the string content}
My question is whether the bind may have issues with certain characters, like a line feed/carriage return or some other combination. I have not set a character encoding in the code so the default encoding is in use.
whether the bind may have issues with certain characters, like a line feed/carriage return or some other combination.
No. There are no issues with bind. The only reason for such error message is when data IS null.
Here goes simple checklist for solving all the problems of the kind.
Trust your eyes. You are not a sole user of mysqli prepared statements. There are millions of them, and noone experienced such a problem yet. Means if database is reporting that value cannot be null, then value IS null.
Anyway, if you still thinks that there is a bug - create an isolated, reprobuceable code example. Means not just create unworkable sketch to show to the readers, but a complete working example that anyone can reproduce. Such as
$mysqli = new mysqli("example.com", "user", "password", "database");
$mysqli->query("CREATE TABLE content ...");
$queryText = "insert into content values (?, ?, ?, ?)";
$query = $mysqli->prepare($queryText);
$dbData[0] = 0;
$dbData[1] = 'some text that yo think is causing the error';
$dbData[2] = 0;
$dbData[3] = 'some another text';
$query->bind_param('dsds',$dbData[0],$dbData[1],$dbData[2],$dbData[3]);
if (!$query->execute()) {
echo '<br>Execute failed: (' . $query->errno . ') . $query->error;
echo '<br>dbData[1]: ' . $dbData[1];
}
without loops. without any code that is not shown - but a complete, working example that produces the noted error.
After failing with such an example, start debugging your code, to find a logical error either with program flow or the way you debug it.
You have to realize that this kind of questions ("I have a bug in PHP out of the clear sky") cannot have another receive without a reproduceable example.
I want to make a simple announcements system where you just type announcements in a box and it can be viewed by others. I get this error when I submit the form:
Edit:
Thanks, the php_announce.php now works, it does everything it's supposed to do, this is the new code :
<?php
// MySQL
$servername = "localhost";
$username = "testuser";
$password = "testpass";
$dbname = "testbase";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Content
$content = $conn->real_escape_string($_POST['content']);
$query="INSERT INTO announcements (content) VALUES ('$content')";
$conn->query($query)
?>
Edit: Thanks for the help I fixed the code like this:
after creating connection it does this:
$result=$conn->query("SELECT * FROM announcements");
#print_r($result);
while($row = $result->fetch_array(MYSQLI_ASSOC)){
echo $row['content']. " <br><br> ". "<b>Posted by: You</b>";
echo "<hr width=100%>";
}
In the first case, you need to provide a list of columns that correspond to the values you're trying to insert. e.g:
$query="INSERT INTO announcements (content) VALUES ('$content')";
Replace content with the name of the column in your table.
For the second one, the error message doesn't appear to correspond with the code you posted, but I'm guessing you forgot a semicolon on the preceding statement. Additionally, there's a missing ; in your last echo statement, although I'm not sure if that's what's causing the specific message you posted. Please repost the latest code from announce_out.php and I'll try to help.
There are some other issues with this approach in general. You're mixing mysqli and the older mysql, which is going to cause you additional errors. Instead of calling mysql_query(), you should be using $conn->query($query) so that you're actually using the connection you are establishing with new mysqli(). Additionally, to prevent injection attacks, you should escape your $content variable in this way:
$content = $conn->real_escape_string($_POST['content']);
This is a pretty basic way to escape strings and there are better methods like prepared statements that mysqli provides. I recommend checking out the page in the PHP manual: http://php.net/manual/en/mysqli.quickstart.php
The immediate answer to your problem is:
You need to escape the content and let the function quote it for you.
$content= mysql_real_escape_string($_POST['content'], $conn);
$query="INSERT INTO announcements VALUES ($content)";
and you need a missing semicolon at the end of the echo statement.
echo $row['content']. " <br><br> ". "<b>Posted by: You</b>";
The long term answer is you should really look into a database abstraction layers which help you avoid incredibly dangerous errors you're likely to make (like incorrectly escaping content). See php manual for some basic ones: http://php.net/manual/en/refs.database.abstract.php
But I also highly recommend using a higher level framework like Doctrine http://www.doctrine-project.org/.
I'm writing a function which will drop a table if it already exists. It will ask the user what they'd like to call the table, take that response and put it into a php variable. I want to make a customized drop statement then for sql so that there are no errors with sql. Here's what I have.
$table = $_POST["tablename"]; //gets table name from html page
drop_table($db, $table); //call function
function drop_table($db,$table){
$drop = "DROP TABLE IF EXISTS .$table. "; //this is the part I can't figure out. How do I add in the table name to the statement,
$q = mysqli_query($db, $drop); //since the sql statement has to be in quotes?
}
Thanks!
P.Ss This is an internal system for analyses only. No worries with dropping tables if just my colleagues and I are using it
Your problem here is a syntax error by attempting to concatenate in $table with dots. Remove those.
$drop = "DROP TABLE IF EXISTS $table ";
But the much much larger problem is that you are permitting end users to drop any table in your database, since you have not filtered the input in any way.
You need to be sure that your users are only dropping tables in the currently selected database, which means at the very least, not permitting . inside $table to prevent things like $table = 'information_schema.user'
if (strpos($table, '.') !== FALSE) {
// don't allow the action!
}
Another step to take would be to verify that the value of $table exists in information_schema.TABLES and belongs to the correct current database before executing the DROP statement.
// If this returns 1, the table exists in the correct database and can be dropped.
// note that $table is escaped here. I didn't fill in the mysqli_query() but obviously
// this is to be executed. It would be even better with a MySQLi prepared statement
"SELECT 1
FROM information_schema.TABLES
WHERE
TABLE_SCHEMA='the_allowed_database'
AND TABLE_NAME='" . mysqli_real_escape_string($db, $table) . "'"`
After passing this check, you would do well to specify a prefix to tables which are flexible in the environment and are therefore permissible to delete, so that a user could not delete every table in the active database. For example, only permit deletion of tables with the prefix usertable_.
if (strpos($table, 'usertable_') !== 0) {
// don't permit deletion
}
This is a very difficult design to secure, and I would recommend you step back and rethink the strategy here. You need to be extremely careful when allowing users to drop tables based on form input.
Do you mean:
$drop = "DROP TABLE IF EXISTS " . $table;
I really, really hope you've thought through the consequences of someone being able to drop tables from your database by entering the right name in the URL.
Well thats pretty much it.
This is my query:
$query = 'ALTER TABLE permissions ADD '.$name.' INT NOT NULL DEFAULT \'0\'';
Where $name is already checked to exist with only lower case alpha letters, and not more than 20 length. Im just starting this out with very simple names.
The next 4 lines of code after that one are:
if($stmt = $db -> prepare($query))
{
$success = $stmt -> execute();
$stmt -> close();
if(!$success)
echo 'ERROR: Unsuccessful query: ',$db->error,PHP_EOL;
}
And I get back, every time
ERROR: Unsuccessful query:
And no error message. Is there a way to get more error messages so I can see what is failing? I can add new columns through phpmyadmin, but that really doesnt help me at all.
The $db is fine, i do lots of stuff before and after this one section. It is only adding new column to the table that fails.
side question: prepare() rejected my query every time when i tried to make those 2 variables, the $name and the 0 value as ? ? prepared statement values. Thats why they are in the real query and not bound later. If i could change that too I would like that.
Try to replace;
$db->error to $stmt->error
And put this before the close().
worth checking you're not trying to use a column or table name that is 'reserved'. for example you cant have a col called 'lon' or 'host'