PHP: mysqli and prepared statement, failing if a field is empty - php

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

Related

Dynamic SQL Inserts for JSON data [PHP]

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.

SQL INSERT INTO table results in duplicates

I have a script that I have setup a CRON for that is getting values from a 3rd party server via JSON (cURL)
Right now every time the cron runs it will INSERT a completely new record. Causing duplicates, and resulting me in manually removing the dups.
How would I go about preventing duplicates, and only update the information that is either missing, or different from the NEW $VAR values?
What I want to do can be expressed like this: IF old value is NOT new value use new value else use old value;
if ($stmt->num_rows !== 1) {
if ($insert_stmt = $mysqli->prepare("
INSERT INTO members (
start_date
)
VALUES (?)"))
{
$insert_stmt->bind_param('s',
$StartDate,
);
if (! $insert_stmt->execute()) { echo ''; }
}
}
}
You should try using INSERT ... ON DUPLICATE KEY UPDATE. Documentation
This does mean that you will have to define some unique (could be primary) key to the table that is always constant so MySQL knows what to update.
A quick example of how you would do it:
INSERT INTO table (f1,f2,f3) VALUES ('something_unique',2,5)
ON DUPLICATE KEY UPDATE f2=2,f3=5
The following statement will be silently ignored if one of the fields with the flags UNIQUE or PRIMARY KEY already exist in the database. If you're searching for INSERT IF NOT EXISTS this is probably what you're looking for:
INSERT IGNORE INTO `members` SET name='Steve',start_date='2015-11-20';
You can also overwrite a record that already exists using REPLACE. If it doesn't yet exist, it will be created:
REPLACE INTO `members` SET name='Steve',start_date='2015-11-20';
Another thing to consider would be INSERT ... ON DUPLICATE KEY UPDATE syntax:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
UPDATE table SET c=c+1 WHERE a=1;
I ended up writing another if statement to check if a unique value existed from incoming and the existing db value existed and leaving it blank to prevent it from importing duplicates. I also wrote a separate file to update where values differentiate between what I am receiving as (new) and what is in the database (old) which actually worked out great for my application.
Here is my answer for anyone else that runs into this issue :)
$prep_stmt = "SELECT * FROM table WHERE column_keys=?";
$stmt = $mysqli->prepare($prep_stmt);
if ($stmt) {
$stmt->bind_param('s',$varvalues);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows == 1) {
if ($insert_stmt = $mysqli->prepare("")) {
$insert_stmt->bind_param('');
if (! $insert_stmt->execute()) {
echo 'shits broke'; }
}
}
else { if ($insert_stmt = $mysqli->prepare("
INSERT INTO table (column_keys)
VALUES (?)")) // you will need a ? per column seperate by a , (?,?,?...?)
{ $insert_stmt->bind_param('s',
$varvalues
); // you will also need to bind a 's' (string) 'i' for num, etc per $var value.
if (! $insert_stmt->execute()) { echo 'shits broke';} //lol
}
}
}
Also a simple error reporting trick I stumbled upon that helped me clean up a few things I overlooked. Just place it at the top of the file, or above you want to debug ;)
error_reporting(E_ALL);

mysql insert ignore, replace or update on duplicate - did insert?

I am using php to 'insert ignore' a row into my database. Is there a way to find out whether a row was inserted?
the code looks like this:
if($stmt = $mysqli->prepare('INSERT IGNORE INTO my_table (key_a, key_b) VALUES (?, ?)'))
{
$stmt->bind_param('ss', 'hello', 'world');
$stmt->execute();
$stmt->close();
}
Thank you guys!
Try like this:
if($stmt->execute())
{
echo "Success";
}
else
{
echo "Error";
}
Also check mysqli::$affected_rows
$mysqli->affected_rows
mysqli::$affected_rows -- mysqli_affected_rows — Gets the number of
affected rows in a previous MySQL operation
Try the following: $mysqli->affected_rows
Link:
/* update rows */
$mysqli->query("UPDATE Language SET Status=1 WHERE Percentage > 50");
printf("Affected rows (UPDATE): %d\n", $mysqli->affected_rows);
MySQL has ON DUPLICATE KEY feature where you can define what to do if your insert fails because of some constrains like unique_key or primary_key being inserted twice.
On that cases, cases, you can trap such errors.
If the query inserts normally, it won't execute this block.
If the query fails, the block will be executed.
Now, you may tweak this feature.
For example, in your table, add one insert_attempts columns with default 0 (zero) value.
And try to execute:
INSERT INTO my_table (key_a, key_b)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE
insert_attempts = insert_attempts+1
;
After all records are successful; SELECT the rows with insert_attempt > 0.
I think the execute() method will return a true or false, so I would suggest more sth. like this (also note that u need to do this before you close the connection):
if ($stmt->execute()) {
echo "success"
}
else
{
echo "Error"
}
, also consider to fetch the statement to see how many lines were affected. You can do that with mysqli_stmt_affected_rows(statement), it will give you the lines affected. If you use it, it couls look like this:
int mysqli_stmt_affected_rows ( mysqli_stmt $stmt )
also read here.

Setting variable's database value to NULL instead of empty string

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 :)

PHP/MySQL Insert null values

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).")");

Categories