This question already has answers here:
Using Mysqli bind_param with date and time columns?
(5 answers)
Closed 1 year ago.
I am trying to use prepared statements to insert a datetime for a library application. Here is the code thus far:
global $dbh;
$query = "INSERT INTO `loan` SET
`title` = (?), //example value - Lord of the Rings
`description` = (?), //example value - Trilogy
`start_date` = (?), //example value 20120701 in String datatype
`end_date` = (?)"; //example value 20120702 in String datatype
$statement = $dbh->prepare($query);
$statement->bind_param("ssss", $title,$description,$startDate,$endDate);
$statement->execute();
print $statement->error; //to check errors
$statement->close();
However, I cannot seem to insert this value into the row. At the same time, somehow the
print $statement->error
does not seem to display any error.
Any help will really do.
UPDATE:
It actually works. I was just referencing the wrong database. But I want to add a little outro for new people who chanced upon this.
Remove all the comments as mentioned in the comments/answers as they will mess up your string.
For DATETIME, remember to specify the datatype as String as MySQL only recognises String datatype. If you are not using prepared queries, this means you have to add '' quotes for the values.
The insertion will result in the date (2012-07-01 00:00:00) format as time is not specified.
Both SQL queries work. INSERT INTO tbl_name SET col_name = value or INSERT INTO tbl_name(col_name) VALUES (value) work.
Try something like this:
global $dbh;
$query = "INSERT INTO loan (title, description, start_date, end_date) VALUES (?,?,?,?)"
$statement = $dbh->prepare($query);
$statement->bind_param("ssss", $title,$description,$startDate,$endDate);
$statement->execute();
print $statement->error; //to check errors
$statement->close();
Assuming your form input for your date is a type input named "date", here is what you do. In php type
$date = $_POST['date']
$query = "INSERT INTO `loan` SET
`title` = (?),
`description` = (?),
`start_date` = ".$date. //Insert variable here
"`end_date` = (?)";
I know it's not good practice to insert a date in one single type input but I am just using this example for simplicity only. The proper way you can figure out yourself.
Related
Here is a snippet from my code:
$stmt = $mysqli->prepare("SELECT DISTINCT model FROM vehicle_types
WHERE year = ? AND make = '?' ORDER by model");
$stmt->bind_param('is', $year, $make);
$stmt->execute();
When I echo out the values for $year and $make, I am seeing values, but when I run this script, I get a null value, and the following warning appears in my log file:
PHP Warning: mysqli_stmt::bind_param(): Number of variables doesn't match number of parameters in prepared statement
In this case, year is in the database in type int(10), and I have tried passing a copy that had been cast as an int, and make is a varchar(20) with the utf8_unicode_ci encoding. Am I missing something?
Your prepared statement is wrong, it should be:
$stmt = $mysqli->prepare("
SELECT DISTINCT model FROM vehicle_types WHERE year = ? AND make = ? ORDER by model
");
$stmt->bind_param('is', $year, $make);
$stmt->execute();
When you prepare a statement, you have to substitute every variable with a question mark without quotes. A question mark within quotes will not be recognized as a placeholder.
The number of question marks must be equal to the number of variables in the bind_param()
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.
I am having trouble inserting null values into date fields into a MySQL table.
Here is the insert query:
$query = 'INSERT INTO table (column_s1, column_s2, column_d1, column_d2)
VALUES ("'.$string1.'", "'.$string2.'", '.$date1.', '.$date2.')';
Columns s1 and s2 take string values and d1 and d2 take dates. When I run this query with only the string fields, there is no problem.
The date values can be either set or null, so I have not included the quotation marks in the query, but have instead added them to the variable earlier on. This is the php code I am using to set the date values:
if (empty($date1)){
$date1 = NULL;
}
else{
$date1part = explode("/",$date1);
$date1 = '"'.$date1part[2].'/'.$date1part[1].'/'.$date1part[0].'"';
}
When the date values are all set, the record is inserted correctly. However, when either of the dates is null, nothing is inserted.
Why can't I just insert null values into MySQL like this?
Try this:
$query = "INSERT INTO table (column_s1, column_s2, column_d1, column_d2)
VALUES ('$string1', '$string2', " . ($date1==NULL ? "NULL" : "'$date1'") . ", " . ($date2==NULL ? "NULL" : "'$date2'") . ");";
so for example if you put this into query:
$string1 = "s1";
$string2 = "s2";
$date1 = NULL;
$date2 = NULL;
result should be:
INSERT INTO table (column_s1, column_s2, column_d1, column_d2) VALUES ('s1', 's2', NULL, NULL);
You should convert the null variable into a NULL string first
Like this:
if(is_null($date1)){
$date1 = 'NULL';
}
If you are using a MySQL date column, you must also specify that it should hold null when creating it, like this:
CREATE TABLE `table` (
id INT NOT NULL AUTO_INCREMENT,
date DATE NULL DEFAULT NULL,
PRIMARY KEY(id)
)
It is also very important that you perform the query with bound parameters, for example using pdo
http://www.php.net/manual/en/pdo.construct.php
http://php.net/manual/en/pdo.prepared-statements.php
How do I insert NULL values using PDO?
Something like this:
$query = 'INSERT INTO table (column_s1, column_s2, column_d1, column_d2)
VALUES (?, ?, ?, ?)';
$stmt = $db->prepare($query);
$stmt->execute(array($string1,$string2,$date1,$date2));
If NULL does not work, just pass your date as "0000-00-00":
$chequeDate = "0000-00-00";
Backslash N is another way to express NULL in MySQL.
Try putting the value (backslash N): \N into one of the parameters like this:
$data1 = "\N";
$sql="insert into tablename set column_s1='" . $data1 .
"', column_s2='" . data2 .
"', column_s3='" . $data3 . "'";
Reference: http://dev.mysql.com/doc/refman/5.1/en/load-data.html
In Derby, If you want to insert values except the ones you have declared Null (column_d1, column_d2), sql:
INSERT INTO DB.table (column_s1, column_s2) VALUES ('s1', 's2');
Probably answer is unneeded at this moment, but I found solution exactly I have been searching. Use an Expression to pass NULL like this:
['some_date_to_update' => new Expression('NULL')]
Hence, MySQL will understand what you want, and save (NULL) in DB instead of storing 0-dates. Hope this will help somebody.
In Mysql DATE data type Default NULL means
Some version set as 0000-00-00
Some version set as 1970-01-01
Years later, if someone is still experiencing this issue, you want to use PDO and bind the variables, everything will be taken care of no need to handle the null variables yourself.
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 have a PHP function which inserts multiple records into MySQL:
function commit_purchase($asset_type_ID, $org_ID, $asset_desc, $asset_cost, $date, $org_to_member_ID, $asset_ID, $purchaser_cur_invest, $purchaser_cred_deb, $purchaser_balance) {
global $db;
$query = "START TRANSACTION;
INSERT INTO assets
(asset_type_ID, org_ID, asset_desc, asset_cost, asset_value, purchase_date, is_approved)
VALUES
(:asset_type_ID, :org_ID, :asset_desc, :asset_cost, :asset_cost, :date, 1);
SET #asset_ID = LAST_INSERT_ID();
INSERT INTO cash_out
(org_to_member_ID, amount, description, date, is_approved, asset_ID)
VALUES
(:org_to_member_ID, :asset_cost, :asset_desc, :date, 1, #asset_ID);
SET #cash_out_ID = LAST_INSERT_ID();
INSERT INTO shares
(asset_ID, member_ID, percent_owner, is_approved)
SELECT assets.asset_ID, pending_asset_shares.member_ID, pending_asset_shares.percent_owner, pending_asset_shares.is_approved
FROM assets, pending_asset_shares
WHERE assets.asset_ID = #asset_ID;
DELETE FROM pending_asset_shares
WHERE asset_ID = :asset_ID;
DELETE FROM pending_assets
WHERE pending_asset_ID = :asset_ID;
INSERT INTO trans_log
(translog_id, trans_type, org_to_member_ID, date, purchaser, asset_ID, cur_invest, cash_out_ID, cred_deb, balance)
VALUES
(DEFAULT, 3, :org_to_member_ID, :date, :org_to_member_ID, #asset_ID, :purchaser_cur_invest, #cash_out_ID, :purchaser_cred_deb, :purchaser_balance);
COMMIT;";
$statement = $db->prepare($query);
$statement->bindValue(':asset_type_ID', $asset_type_ID);
$statement->bindValue(':org_ID', $org_ID);
$statement->bindValue(':asset_desc', $asset_desc);
$statement->bindValue(':asset_cost', $asset_cost);
$statement->bindValue(':date', $date);
$statement->bindValue(':org_to_member_ID', $org_to_member_ID);
$statement->bindValue(':purchaser_cur_invest', $purchaser_cur_invest);
$statement->bindValue(':purchaser_cred_deb', $purchaser_cred_deb);
$statement->bindValue(':purchaser_balance', $purchaser_balance);
$statement->bindValue(':asset_ID', $asset_ID);
$statement->execute();
$statement->closeCursor();
return $asset_ID;
I am trying to use the first INSERT statment's LAST_INSERT_ID (#asset) as a variable for my next function. The way I am calling the above function, in hopes of setting the variable, is:
$asset_ID = commit_purchase($asset_type_ID, $org_ID,.......etc.)
I am pretty sure my problem is somewhere around the "return $asset_ID" in my SQL statement. I have been able to do this successfully when using only 1 LAST_INSERT_ID call.
Nothing is being returned at all.
Ok, as mentioned in my comments, you can use beginTransaction to break this up. http://php.net/manual/en/pdo.begintransaction.php
Once you have done that, it's just a matter of getting the last inserted ID. You can use lastInsertId for that: http://php.net/manual/en/pdo.lastinsertid.php
Breaking this down into multiple queries would really be the best solution, but to answer your original question: If you want to get the value of MySQL variables in PHP, just execute a SELECT query:
$asset_ID = mysql_result( mysql_query( 'SELECT #asset_ID' ) );