I have a table with a lot of columns. I have a function that duplicates my record into a new row with an updated auto-incremented ID.
The function works perfectly however, on some of my INT columns, sometimes I have a NULL as the default. When the record is duplicated, it turns my NULL placement into a 0.
I'm assuming it's because I am doing '" . value . "'
Could anyone help me figure out how I could make NULL values be inserted as " . NULL . " and keep my other values as '" . value . "'?
EDIT I'm having trouble differentiating a null and a blank value. I've tried empty() and is_null() and a varchar with no value and an INT with a NULL value isn't showing a difference
note: I understand that I am using an outdated mysql extension. For right now, I'm just trying to process my null variables correctly.
function duplicateRow($table, $id_field, $id_value)
{
// copy content of the record you wish to clone
$entity = mysql_fetch_array(mysql_query("SELECT * FROM {$table} WHERE {$id_field}={$id_value}"), MYSQL_ASSOC) or die("Could not select original record");
// set the auto-incremented id's value to blank. If you forget this step, nothing will work because we can't have two records with the same id
$entity[$id_field] = "";
// insert cloned copy of the original record
mysql_query("INSERT INTO {$table} (".implode(", ",array_keys($entity)).") VALUES ('".implode("', '",array_values($entity))."')") or die(mysql_error());
//returns the new id
return mysql_insert_id();
}
You don't need to fetch the data into PHP only then to send it back to MySQL: INSERT ... SELECT is a single SQL command that enables the whole shebang to take place natively within the database.
However, you need to exclude the $id_field from the operation, so you can't use the * wildcard but must instead explicitly list the column names. This adds some complexity, especially to perform the operation in a safe, injection-proof way:
function duplicateRow($table, $id_field, $id_value)
{
// prevent SQL injection
$enc_map = array(
'utf8' => 'UTF-8',
'latin1' => 'Windows-1252' // etc.
);
mb_regex_encoding($enc_map[mysql_client_encoding()]);
$table_safe = '`'.mb_ereg_replace('`', '``', $table ).'`';
$id_field_safe = '`'.mb_ereg_replace('`', '``', $id_field).'`';
$id_value_safe = mysql_real_escape_string($id_value);
// fetch column names
$fields = array();
$qry = mysql_query("SHOW COLUMNS FROM $table_safe");
while ($row = mysql_fetch_assoc($qry))
if ($row['field'] != $id_field)
$fields[] = '`'.mb_ereg_replace('`', '``', $row['field']).'`';
$fields_safe = implode(',', $fields);
// duplicate the record
mysql_query("
INSERT INTO $table_safe
($fields_safe)
SELECT $fields_safe
FROM $table_safe
WHERE $id_field_safe = '$id_value_safe'
");
//returns the new id
return mysql_insert_id();
}
Note that the ancient ext/mysql extension has been deprecated and its use in new code has been discouraged for years. You should seriously consider switching to either MySQLi or PDO.
What is your MySQL Version? some versions of MySQL (5.5 and earlier if I'm not mistaken) convert null values to empty for string fields and 0 to int fields.
You have to force null value or update to MySQL 5.6
I ended up making a foreach statement that checked to see if my value was null and changed the query a little bit. This may not be the greatest, the right, or practical way to do this but it works. If anyone has any suggestions, they are 100% appreciated!
The reason I kept my function mostly the same is I needed to use this for multiple large tables. So I didn't know exactly what the fields were going to be.
function duplicateRow($table, $id_field, $id_value)
{
// copy content of the record you wish to clone
$entity = mysql_fetch_array(mysql_query("SELECT * FROM {$table} WHERE {$id_field}={$id_value} AND snapshot=0"), MYSQL_ASSOC) or die("Could not select original record");
foreach ($entity as &$value) { if(is_null($value) == true) { $value = "NULL"; } else { $value = "'$value'"; } }
// set the auto-incremented id's value to blank. If you forget this step, nothing will work because we can't have two records with the same id
$entity[$id_field] = "'";
// insert cloned copy of the original record
$query = "INSERT INTO {$table} (".implode(", ",array_keys($entity)).") VALUES ('".implode(", ",array_values($entity)).")";
mysql_query($query) or die(mysql_error());
//returns the new id
return mysql_insert_id();
}
Related
I am looking for some inspiration from someone wiser than me with PHP/MySQL.
In have a database application, and in this instance there are two primary tables and one child table.
Primary Table 1 - Documents
Primary Table 2 - JobDesriptions
Child Table - LnkDocuments_JobDescriptions, which as the title suggests is a one to many relational table between the Document and Job Description Table. In my Documents Table I have a field which is a lookup of JobDescriptions and presents options as a checkbox, this field is called 'AppliesTo', because of the way the application works, the field stores the results as a string, eg "1,2,3,4,5) I have used the explode function to turn this into an array and then insert each record into the child table, as I prefer 1-2-many relationships. This is the code that I have, and it works.
$jdarray = explode(',',$values['AppliesTo']);
foreach($jdarray as $item)
{
$sql2 = "INSERT INTO LnkDocuments_JobDescriptions (DocumentFk, JobDescriptionFk)
values ('".$keys["DocumentPk"]."', '$item')"; CustomQuery($sql2);
}
The problem I now have is that if that table gets updated, I need to also update the child table, i have tried this code (but quickly realised that it is wrong):
$jdarray = explode(',',$values['AppliesTo']);
foreach($jdarray as $item)
{
$sql = "SELECT * FROM LnkDocuments_JobDescriptions WHERE DocumentFk='".$keys["DocumentPk"]."' AND JobDescriptionFk='$item'"; ;
$num_rows = mysql_num_rows(CustomQuery($sql));
if ($num_rows > 0) //Delete Record
{
$sql2 = "DELETE FROM LnkDocuments_JobDescriptions WHERE DocumentFk='".$keys["DocumentPk"]."' AND JobDescriptionFk='$item'"; CustomQuery($sql2);
echo "JD FK : ".$item." deleted";
}
else //Insert Record
{
$sql3 = "INSERT INTO LnkDocuments_JobDescriptions (DocumentFk, JobDescriptionFk)
values ('".$keys["DocumentPk"]."', '$item')"; CustomQuery($sql3);
echo "JD FK : ".$item." added";
}
}
It occured to me that I need to compare differences in the arrays, but havent got a clue how to do this, but this is what I need:
If I can get $oldarray and $new array to compare, for example
if in old array there were values 1,2,3,4 and in $newarray there were values 1,2,3,5, I want the code to loop through each value to determine if there is a change, e.g. if value exists in old and new array then do nothing, if value exists in old array but not new then delete, if value exists in new array but not old then insert.
I have also thought about just deleting all associated records and adding again, but think this is bad practice and will result in high number primary key, also it is worth noting that in my example there are only 5 options, this is just for testing, in reality there could be dozens.
Thanks in advance
If you are trying to optimize things I'm not sure that reading the values already present in the table and then deleting only those are not in the new version while inserting the missing records is the best way to go. In my opinion it would be much faster to just delete everything in one query, then insert all records in one query. Try something like this:
$item_list = implode( ',' , $jdarray );
$delete_query = "DELETE FROM LnkDocuments_JobDescriptions WHERE DocumentFk='".$keys["DocumentPk"]."' AND JobDescriptionFk IN ( $item_list )";
CustomQuery($delete_query);
$document_key = "'" . $keys["DocumentPk"] . "'";
$item_list_to_insert = "($document_key, " . implode( "), ($document_key, ", $jdarray ) . ")";
$insert_query = "INSERT INTO LnkDocuments_JobDescriptions (DocumentFk, JobDescriptionFk) VALUES " . $item_list_to_insert;
CustomQuery($insert_query);
Note: I didn't test this, there might some debugging needed.
I have the below code, which works perfect. What i want to do is to check the refNo first to see if there are duplicates entries in MySQL. If there is then appear a warning message, otherwise appear a "ok" message. How can i do that with PDO? Any help?
(include("db.php"));
$SQLquery = "INSERT INTO mydatabase (refNo, name)
VALUES ('".$_POST["refNo"]."', '".$_POST["name"]."');";
$STH = $dbc->query($SQLquery);
?>
edit: Hello guys,
i prefer not to add primary keys. Is there any other way?
Set up refNo as a primary key. You could also create it as unique but that defeats the purpose - your reference number appears to be a unique primary identifier. Perfect choice for a primary key.
Further, change your query
try {
$SQLquery = "INSERT INTO mydatabase (refNo, name) VALUES (:refNo, :name)";
$SQLquery = $dbc->prepare($SQLquery);
$SQLquery->bindValue(':refNo', $_POST['refNo']);
$SQLquery->bindValue(':name', $_POST['name']);
$SQLquery->execute();
} catch (Exception $e) {
die("Insert error");
}
$count = $SQLquery->rowCount();
if ($count == 1) {
echo "Record added!";
}
This binds the post value to prevent SQL injection too.
Edit: You could follow this up with $count = $SQLquery->rowCount(); which will be 1 if the insert was successful, as it appears you've edited your question since you posted it for more info.
If you want to do this without using a database level constraint, you'll need to do an extra SELECT statement before inserting into the table. But that gives you no absolute guarantees, as it might be two processes want to insert the same row at the same time and they will still succeed.
-- it'll look a little something like this; I'm not familiar with PDO but the structure should be the same
$selectQuery = "SELECT * FROM mydatabase
WHERE refno = '".$_POST["refNo"]."'";
$res = $dbc->query( $selectQuery );
if( $res->count() > 0 ) {
// this result already exists; show error
}
else {
// this result is new; put the insert query here
}
I have a variable which displays the following string:
$item_value = itemOne,itemTwo,itemThree
I would like to take this string and have insert each item as a separate row entry for a single column. Additionally, I need it to insert an auto increment key value for each entry. So to complete this example, here is what I would want the mysql table to look like when complete:
ID || item_value || comments
----------------------------------------
1 || itemOne || --------------
2 || itemTwo || --------------
3 || itemThree || --------------
My feeling is that I need to explode the string around the comma and then insert it into the table. I have attempted this but am having some issues getting each item as separate row entries. Any assistance is much appreciated.
For the auto-increment i suggest letting the database handle it, for mysql just declare it with AUTO_INCREMENT on the id field, for postgres you can set the data type to serial, as for separating each line use the php explode function
here's a little example
<?php
$dbh = new PDO('mysql:host=localhost;dbname=database', 'username', 'password');
$query = "INSERT into my_table (item_value) VALUES (?)";
$data = 'itemOne,itemTwo,itemThree,itemFour';
$st = $dbh->prepare($query);
foreach(explode(',', $data) as $r) {
// user array($r) for php 5.3 or lower
$st->execute([$r]);
}
This uses PDO which is the recommended method for handling database connections
Something like this should split them up and give the option for insertion in the db:
<?php
$item_value = 'itemOne,itemTwo,itemThree';
$item_array = explode(",",$item_value);
foreach($item_array as $key => $value){
// insert into the db here
$query = "INSERT INTO table_name set item_value = '".mysql_real_escape_string($value)."', ID = '".($key + 1)."'";
// however you choose to connect and insert into the database goes here :)
}
?>
What is the easiest / most efficient way to get the entire row inserted after an INSERT statement?
I am pretty sure I could do this as follows:
$aQuery = "INSERT into myTable (a, b, c) VALUES (1, 'Hello', 'Goodbye')";
//the IDENTITY coloumn in myTable is named id
$result = sqlsrv_query($myConn, $aQuery );
if ($result) {
$res = sqlsrv_query('SELECT LAST_INSERT_ID()');
$row = sqlsrv_fetch_array($res);
$lastInsertId = $row[0];
$subQuery = "SELECT * FROM myTable where id = {$lastInsertId}";
$subResult = sqlsrv_query($myConn, $subQuery);
if ($subResult) {
while($subrow = sqlsrv_fetch_array($subResult)) {
echo($subrow ['id'] . ', '.
$subrow ['a'] . ', '.
$subrow ['b']); //etc...
}
}
}
However, I am concerned about the possibility of another insert occurring just before my SELECT LAST_INSERT_ID() and thus messing up my logic to boot. How can I be certain that the last inserted id is truly the INSERT I called previously, and not something happening somewhere else?
Is there a more appropriate way of doing this, perhaps a complete SQL solution (such that the query returns the row automatically rather than using PHP)?
UPDATE: myTable DOES have an explicitly defined (and auto-incremented) identity column, named id.
This will work:
"INSERT into myTable (a, b, c) OUTPUT Inserted.a, Inserted.b, Inserted.c VALUES (1, 'Hello', 'Goodbye')
In Sql Server, you would use select #lastID=SCOPE_IDENTITY()
And #LastID will have the last id inserted for the current scope; therefore, if there was another insertion in the middle, you would still get the correct record on your select.
Never use ##Identity for this or you may end up in a situation like you described.
If you were to use identity field (which maybe you should) there is a command called SCOPE_IDENTIY() which info you can find here:
http://msdn.microsoft.com/en-us/library/ms190315.aspx
Since you do not use it, you do not have to select latest data since you have it when you insert, so just use same data instead of selecting.
Trying to check if a name is already stored in the database from the login user. The name is a set of dynamic arrays entered by the user threw a set of dynamic form fields added by the user. Can some show me how to check and see if the name is already entered by the login user? I know my code can't be right. Thanks!
MySQL code.
SELECT *
FROM names
WHERE name = '" . $_POST['name'] . "'
AND userID = '$userID'
Here is the MySQL table.
CREATE TABLE names (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
userID INT NOT NULL,
name VARCHAR(255) NOT NULL,
meaning VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
If $_POST['name'] is actually an array of strings, as you say, then try this PHP:
$namesString = '';
foreach ($i=0; $i < count($_POST['name']) $i++)
{
$namesString .= "'" . mysql_real_escape_string($_POST['name'][$i]) . "'";
if(isset($_POST['name'][$i + 1]) $nameString .= ', ';
}
With this query:
SELECT * FROM `names`
WHERE `name` IN ( $namesString )
AND `userID` = '$userID'
The query will return all the rows in which the name is the same as string in $_POST['name'].
First of all, if the userID field is unique, you should add a unique index on it in your table.
Also, watch out for SQL injection attacks!
Using something like this is much more secure:
$sqlQuery = sprintf('SELECT COUNT(id) AS "found" FROM names WHERE userID = "%s"', mysql_real_escape_string($_POST['name'], $conn));
This SQL query will return 1 row with 1 field (named found) which will return you the number of matched rows (0 if none). This is perfect if you only want to check if the userID exists (you don't need to fetch all data for this).
As for the dynamic array, you will have to post more information and I'll update my answer.
Meanwhile here are some usefull PHP functions that can help you do what you want:
For MySQL queries:
mysql_connect
mysql_real_escape_string
mysql_query
mysql_fetch_assoc
For your list of users:
explode
implode
Stated as you say, I'm quite sure the code does exactly what you are asking for. The SELECT should return the records that respond both to the name sent and the current user ID.
If you need some php code, here it is (should be refined):
$result = mysql_query('YOUR SELECT HERE');
if (!$result) {
die('ERROR MESSAGE');
} else {
$row = mysql_fetch_assoc($result));
// $row is an associative array whose keys are the columns of your select.
}
Remember to escape the $_POST.