I'm struggling with some PHP/MySQL code. I am reading from 1 table, changing some fields then writing to another table, nothing happens if inserting and one of the array values is null when I would like it to insert null in the database (null values are allowed for the field). It looks a bit like this:
$results = mysql_query("select * from mytable");
while ($row = mysql_fetch_assoc($results) {
mysql_query("insert into table2 (f1, f2) values ('{$row['string_field']}', {$row['null_field']});
}
Not every row has a null value and in my query there are more fields and 2 columns which may or may not be null
This is one example where using prepared statements really saves you some trouble.
In MySQL, in order to insert a null value, you must specify it at INSERT time or leave the field out which requires additional branching:
INSERT INTO table2 (f1, f2)
VALUES ('String Value', NULL);
However, if you want to insert a value in that field, you must now branch your code to add the single quotes:
INSERT INTO table2 (f1, f2)
VALUES ('String Value', 'String Value');
Prepared statements automatically do that for you. They know the difference between string(0) "" and null and write your query appropriately:
$stmt = $mysqli->prepare("INSERT INTO table2 (f1, f2) VALUES (?, ?)");
$stmt->bind_param('ss', $field1, $field2);
$field1 = "String Value";
$field2 = null;
$stmt->execute();
It escapes your fields for you, makes sure that you don't forget to bind a parameter. There is no reason to stay with the mysql extension. Use mysqli and it's prepared statements instead. You'll save yourself a world of pain.
I think you need quotes around your {$row['null_field']}, so '{$row['null_field']}'
If you don't have the quotes, you'll occasionally end up with an insert statement that looks like this: insert into table2 (f1, f2) values ('val1',) which is a syntax error.
If that is a numeric field, you will have to do some testing above it, and if there is no value in null_field, explicitly set it to null..
For fields where NULL is acceptable, you could use var_export($var, true) to output the string, integer, or NULL literal. Note that you would not surround the output with quotes because they will be automatically added or omitted.
For example:
mysql_query("insert into table2 (f1, f2) values ('{$row['string_field']}', ".var_export($row['null_field'], true).")");
Related
Near daily I am tasked with inserting JSON data into a relational database via PHP, as is with JSON data some records with have certain columns while others do not, and this tends to be a problem when inserting into a table.
If I am inserting several thousands students a record might look like
{"name": "Billy Jackson", "Height": 172, "DOB" : "2002-08-21"}
However its not certain that height and or DOB is set in any record, what I currently do is something like
<?php
foreach($records as $json){
$name = addslashes($json['name']);
if(isset($json['Height']){
$height = $json['Height'];
}
else{
$height = "NULL"
}
if(isset($json['DOB']){
$dob = $json['DOB'];
}
else{
$dob = "NULL"
}
}
$db->query("INSERT INTO table (name, height, dob) VALUES ('$name', $height, '$dob')");
As you may see this is not elegant nor does it work for several types, fields like DOB do not accept NULL, nor do enums.
Is there a more elegant built in solution, to only try and insert into columns where the value exists in the JSON.
Is this something prepared statements handle?
EDIT
lets say the example record above did not have DOB setthe insert statement would look like
"INSERT INTO table (name, height, dob) VALUES ('Billy Jackson', 172, 'NULL')"
Which fails, if have $dob be set to null ($dob = null) if it is not set then the insert statement looks like
"INSERT INTO table (name, height, dob) VALUES ('Billy Jackson', 172, '')"
Which fails
Why even include the dob column? because some records do have a dob and I want them included in the insert
Empty string '' is not the same as null. Nor is the string "null". Since your query explicitly quotes the contents of the $dob variable, you're quoting the string null such that it becomes "null" which is definitely not null. :)
To avoid the need to mess with quotes (and SQL injection), you'll want to use a prepared statement, something like this:
$db->prepare('INSERT INTO table (name, height, dob) VALUES (?, ?, ?)');
Then when you bind the values, PHP will automatically take care of what fields need quotes and which don't.
Also note, you can shortcut this:
if (isset($json['Height']){
$height = $json['Height'];
} else {
$height = "NULL"
}
Into just this:
$height = $json['Height'] ?? null;
Which would eliminate a bunch of your code and make your bind something like this:
$stmt->bind_param(
'sis',
$json['name'],
$json['Height'] ?? null,
$json['dob'] ?? null
);
You should start with addressing the problems in your table design.
All columns that MUST have data should be set to NOT NULL, and a default value set, if appropriate. It may not be appropriate to have a default value for User Name, for example, so don't set one.
All columns that MIGHT have data should be set to accept NULL, with a default value set as appropriate. If there's no data then the correct value should generally be NULL and that should be set as a default.
Note that both DATE and ENUM columns can accept NULL if properly configured.
Once you have your column definitions correct you can generate an INSERT query based on the actual values you find in your JSON file. The data integrity rules you set in your table definition will ensure that appropriate values are entered for any row that is created with values missing, or that the row is not created if 'must have' data is missing.
This leads to some code like this, based on PDO prepared statements:
$json = '{"name": "Billy Jackson", "Height": 172, "DOB" : "2002-08-21"}';
$columnList = [];
$valueList = [];
$j = json_decode($json);
foreach($j as $key=>$value) {
$columnList[] = $key;
// interim processing, like date conversion here:
// e.g if $key == 'DOB' then $value = reformatDate($value);
$valueList[] = $value;
}
// Now create the INSERT statement
// The column list is created from the keys in the JSON record
// An array of values is assembled from the values in the JSON record
// This is used to create an INSERT query that matches the data you actually have
$query = "INSERT someTable (".join(',',$columnList).") values (".trim(str_repeat('?,',count($valueList)),',').")";
// echo for demo purposes
echo $query; // INSERT someTable (name,Height,DOB) values (?,?,?)
// Now prepare the query
$stmt = $db->prepare($query);
// Execute the query using the array of values assembled above.
$stmt->execute($valueList);
Note: You many need to extend this to handle mapping from JSON keys to column names, format changes in date fields, etc.
Using PHP Version 7.1.9, MariaDB 10.1.26.
I'm submitting form data to a MySQL database, one of my values is NULL however in the database it's empty.
I have ensured that my database table is set to;
allow null = yes
default - null
My code is below (please ignore any security vulnerabilities this is simplified code);
$id = $_POST['id '];
$name = $_POST['name'] ? $_POST['name'] : NULL ;
$sql = "INSERT INTO staff (id, name) VALUES ('".$id."', '".$name."')
// query runs and inserts successfully
When I var_dump($name) I get NULL, although the name value in my database is empty (i.e. not null)
Any ideas what i'm doing wrong?
Edit
The original poster said
My code is below (please ignore any security vulnerabilities this is simplified code)
I interpret that as "I know about SQL injection and I am taking measures to prevent it in my code. I've simplified my post to make it easier to get an answer."
My response below is following their format. That's why I did not use PDO, mysqli, prepared statements/escape measures in my post. If I were personally writing code to insert data into a database, I would make sure my data is sanitized and I would use an ORM like Doctrine (which is a wrapper for PDO) to interact directly with the database.
My Answer
Referencing the code in the original post:
$id = $_POST['id '];
$name = $_POST['name'] ? $_POST['name'] : NULL ;
$sql = "INSERT INTO staff (id, name) VALUES ('".$id."', '".$name."')
// query runs and inserts successfully
Your query is behaving the way you've written your code. If you echo/print a PHP variable to standard output after it has been set to NULL you won't see a value at all. Null is the absence of value. Since you've wrapped the absence of value (no value, null) in single quotes, you're telling MySQL that you want to insert an empty string into the name column.
I would rewrite the code as follows:
$id = $_POST['id '];
$name = $_POST['name'] ? "'$_POST[name]'" : 'NULL';
$sql = "INSERT INTO staff (id, name) VALUES ('$id', $name)";
Notice how I put NULL in a string for the name variable. When I include the name variable in the query I don't wrap it with quotes. This is the proper way to explicitly add a null value to a column in MySQL.
PHP's double quotes allows variable interpolation. This means you don't have to break your strings down into individual parts and concatenate string values together. This makes the code cleaner and easier to read.
First, you're obviously not using prepared statements. I strongly advice you to use prepared statements in the name of security and stability.
Then, on to the issue at hand. The database doesn't know what a PHP null is and will only see an empty string to be inserted in your code.
"" . null . "" === ""
Keeping your (very dangerous and vulnerable) example code, and modifing the place where you add the "quotes" around the to be inserted string. If the name is null just insert NULL without quotes around it. the databse server will interpret that as having to inserta null value
$name = $_POST['name'] ? "'".$_POST['name']."'" : 'NULL';
$sql = "INSERT INTO staff (id, name) VALUES ('".$id."', ".$name.")";
Now really, investigate how to do prepared queries to prevent SQL injections
or at least use mysqli_real_escape_string or something equivalent.
this is the more secure version, using PDO.
$sql = "INSERT INTO staff (id,name) VALUES (:id,:name)";
$stmt= $dpo->prepare($sql);
$stmnt->bindParam(':id', $id, PDO::PARAM_INT);
if(!$POST['name']) {
$stmnt->bindParam(':name', null, PDO::PARAM_NULL);
}
else {
$stmnt->bindParam(':name', $POST['name'], PDO::PARAM_STR);
}
$stmt->execute();
I would instead use PDO prepared statements. That should set the value to NULL instead of an empty string. Because you are wrapping '".$name"' it is making the query '') - i.e an empty string.
I'd do like this:
$id = $_POST['id '];
if(isset($_POST['name'])){
$sql = "INSERT INTO staff (id, name) VALUES ('".$id."', '".$name."')
}else{
$sql = "INSERT INTO staff (id) VALUES ('".$id."')
}
discriminating the query if I receive the name from the form or not.
You should use prepared statements to pass variables (user inputs) to a mysql query. Otherwise you are widely open to SQL injections.
I am using mysqli to insert a new row into a table.
$stmt = $con->prepare("INSERT INTO `table` (field1, field2) VALUES (?, ?)");
field 1 and 2 come from a post request.
But if field 2 is not set the entire row is not inserted, how can I change this behaviour so it still inserts field 1.
You can change this behavior by allowing Null value for field 2 in Database. Or you can assign an empty string to the variable if it is NULL.
if(!(isset($_POST['field2']))
{
$field2="";
}
else
{
$field2=$_POST['field2']);
}
first you check field is set or not set
if(!(isset($_POST['field2']))
{
$field2="";
}
else
{
$field2=$_POST['field2']);
}
//and then you execute the query
In my SQL database there're many fields like this:
Field Type:Text Null:Yes Default:NULL
My INSERT looks like this:
INSERT INTO tbl (col,col,col,...) VALUES ('val','val','val',...)
Now, those quotes in my INSERT statement's values are inserting '' (empty string) in to the database when what I really want is nothing (NULL).
So I tried
if (isset($_POST['title'])) {$newTitle = mysql_real_escape_string(trim($_POST['title']));} else {$newTitle = NULL;}
and that just inserts 'NULL' - the string containing the word NULL.
What can I do to be certain my NULL values are inserted properly?
What you have is fine, but you need to combine it with a prepared statement...
// prepare the statement
$stmt = $mysqli->prepare("INSERT INTO tbl (title, x,y,z) values (?,?,?,?)");
$stmt->bind_param($newTitle, $x,$y,$z);
$x = 'hello, world';
// execute prepared statement
$stmt->execute();
If x or newTitle are NULL, they will be NULL in the DB
You can try by adding a NULL without the quotes example below:
INSERT INTO tbl (col,col,col,...) VALUES (NULL,'val','val',...)
Also make sure the column that you want to have a pure null must have the allowed NULL ticked.
Don't specify the field in INSERT INTO or provide a value.
If you have 3 fields, f1 f2 f3
And you
INSERT INTO tbl (f1, f3) VALUES ('something', 'something')
Then f2 will not be inserted and default to null.
I use '0' instead of null. When you use if statements you can run queries like
if($row['f2'] == 0){...
Rather than null :)
Sometimes I need to insert into the table some null values, or update them setting the value to NULL.
I've read somewhere in the Postgres documentation that this can't be done, but can be tricked with the default value:
pg_query("INSERT INTO my_table (col_a, col_b) VALUES ('whatever', default)
I know that in this example I'll have the same result with:
pg_query("INSERT INTO my_table (col_a) VALUES ('whatever')
But the problem comes with prepared statements:
pg_prepare($pgconn, 'insert_null_val', "INSERT INTO my_table (col_a, col_b) VALUES ($1, default)");
pg_exec($pgconn, 'insert_null_val', array('whatever'));
//this works, but
pg_prepare($pgconn, 'insert_null_val', "INSERT INTO my_table (col_a, col_b) VALUES ($1, $2)");
pg_exec($pgconn, 'insert_null_val', array('whatever', 'NULL'));
//insert into the table the string 'NULL'.
//instead using array('whatever', '') it assume the col_b as empty value, not NULL.
The same problem applies to update statements.
I think there is a solution, because pgmyadmin can do that (or it seems like it can).
If you are wondering why I need to play with null values in my tables, let me throw an example (maybe there is a way better then the null value?):
Assume I have the users table with an email column, which can be empty, but has a unique index. 2 empty emails are equal and violate the unique constraint, while 2 NULL values are not equal and can coexist.
Use the php's literal NULL as a parameter:
pg_prepare($pgconn, 'insert_null_val', "INSERT INTO my_table (col_a, col_b) VALUES ($1, $2)");
pg_query($pgconn, 'insert_null_val', array('whatever', NULL));