This is not a finished patch - do not apply it to a production server.
This is provided in the hope that it will help towards a solution to edited records disappearing until approved.
The 'view list' works, but not 'view single'.

diff --git a/mod/data/backup/moodle2/backup_data_stepslib.php b/mod/data/backup/moodle2/backup_data_stepslib.php
index 691b059..2a154a9 100644
--- a/mod/data/backup/moodle2/backup_data_stepslib.php
+++ b/mod/data/backup/moodle2/backup_data_stepslib.php
@@ -58,7 +58,7 @@ class backup_data_activity_structure_step extends backup_activity_structure_step
 
         $record = new backup_nested_element('record', array('id'), array(
             'userid', 'groupid', 'timecreated', 'timemodified',
-            'approved'));
+            'approved', 'replaces'));
 
         $contents = new backup_nested_element('contents');
 
@@ -109,6 +109,7 @@ class backup_data_activity_structure_step extends backup_activity_structure_step
 
         $record->annotate_ids('user', 'userid');
         $record->annotate_ids('group', 'groupid');
+        $record->annotate_ids('data_records', 'replaces');
 
         $rating->annotate_ids('scale', 'scaleid');
         $rating->annotate_ids('user', 'userid');
diff --git a/mod/data/backup/moodle2/restore_data_stepslib.php b/mod/data/backup/moodle2/restore_data_stepslib.php
index 15af06a..9aef4a1 100644
--- a/mod/data/backup/moodle2/restore_data_stepslib.php
+++ b/mod/data/backup/moodle2/restore_data_stepslib.php
@@ -102,6 +102,7 @@ class restore_data_activity_structure_step extends restore_activity_structure_st
         $data->userid = $this->get_mappingid('user', $data->userid);
         $data->groupid = $this->get_mappingid('group', $data->groupid);
         $data->dataid = $this->get_new_parentid('data');
+        $data->replaces = $this->get_mappingid('data_record', $data->replaces);
 
         // insert the data_records record
         $newitemid = $DB->insert_record('data_records', $data);
diff --git a/mod/data/db/install.xml b/mod/data/db/install.xml
index 58f086e..7cb7d2a 100644
--- a/mod/data/db/install.xml
+++ b/mod/data/db/install.xml
@@ -81,7 +81,8 @@
         <FIELD NAME="dataid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="groupid" NEXT="timecreated"/>
         <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="dataid" NEXT="timemodified"/>
         <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timecreated" NEXT="approved"/>
-        <FIELD NAME="approved" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timemodified"/>
+        <FIELD NAME="approved" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timemodified" NEXT="replaces"/>
+	<FIELD NAME="replaces" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="approved"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="dataid"/>
@@ -106,4 +107,4 @@
       </KEYS>
     </TABLE>
   </TABLES>
-</XMLDB>
\ No newline at end of file
+</XMLDB>
diff --git a/mod/data/db/upgrade.php b/mod/data/db/upgrade.php
index 1d85846..5301388 100644
--- a/mod/data/db/upgrade.php
+++ b/mod/data/db/upgrade.php
@@ -313,6 +313,16 @@ function xmldb_data_upgrade($oldversion) {
         upgrade_mod_savepoint(true, 2010100101, 'data');
     }
 
+    if ($oldversion < 2010120100) {
+        $table = new xmldb_table('data_records');
+        $field = new xmldb_field('replaces', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'approved');
+
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+        upgrade_mod_savepoint(true, 2010120100, 'data');
+    }
+
     return true;
 }
 
diff --git a/mod/data/edit.php b/mod/data/edit.php
index 34aef3f..f2048b3 100755
--- a/mod/data/edit.php
+++ b/mod/data/edit.php
@@ -158,35 +158,72 @@ if ($datarecord = data_submitted() and confirm_sesskey()) {
 
     if ($rid) {                                          /// Update some records
 
-        /// All student edits are marked unapproved by default
+        $copyid = false;
         $record = $DB->get_record('data_records', array('id'=>$rid));
+        if ($data->approval && !has_capability('mod/data:approve', $context) && $record->approved) {
+            // See if there is already an entry awaiting approval
+            $record = $DB->get_record('data_records', array('replaces'=>$rid));
 
-        /// reset approved flag after student edit
-        if (!has_capability('mod/data:approve', $context)) {
-            $record->approved = 0;
+            if (!$record) {
+                // Need to create an entry for approval
+                $copyid = data_add_record($data, $currentgroup, $rid);
+            }
         }
+        
+        if ($copyid) {
+            // Equivalent to creating a new record
 
-        $record->groupid = $currentgroup;
-        $record->timemodified = time();
-        $DB->update_record('data_records', $record);
+            /// Insert a whole lot of empty records to make sure we have them
+            $fields = $DB->get_records('data_fields', array('dataid'=>$data->id));
+            foreach ($fields as $field) {
+                $content->recordid = $copyid;
+                $content->fieldid = $field->id;
+                $DB->insert_record('data_content',$content);
+            }
 
-        /// Update all content
-        $field = NULL;
-        foreach ($datarecord as $name => $value) {
-            if (!in_array($name, $ignorenames)) {
-                $namearr = explode('_',$name);  // Second one is the field id
-                if (empty($field->field) || ($namearr[1] != $field->field->id)) {  // Try to reuse classes
-                    $field = data_get_field_from_id($namearr[1], $data);
+            /// For each field in the add form, add it to the data_content.
+            foreach ($datarecord as $name => $value){
+                if (!in_array($name, $ignorenames)) {
+                    $namearr = explode('_', $name);  // Second one is the field id
+                    if (empty($field->field) || ($namearr[1] != $field->field->id)) {  // Try to reuse classes
+                        $field = data_get_field_from_id($namearr[1], $data);
+                    }
+                    if ($field) {
+                        $field->update_content($copyid, $value, $name);
+                    }
                 }
-                if ($field) {
-                    $field->update_content($rid, $value, $name);
+            }
+
+        } else {
+            /// All student edits are marked unapproved by default
+
+            /// reset approved flag after student edit
+            if (!has_capability('mod/data:approve', $context)) {
+                $record->approved = 0;
+            }
+
+            $record->groupid = $currentgroup;
+            $record->timemodified = time();
+            $DB->update_record('data_records', $record);
+
+            /// Update all content
+            $field = NULL;
+            foreach ($datarecord as $name => $value) {
+                if (!in_array($name, $ignorenames)) {
+                    $namearr = explode('_',$name);  // Second one is the field id
+                    if (empty($field->field) || ($namearr[1] != $field->field->id)) {  // Try to reuse classes
+                        $field = data_get_field_from_id($namearr[1], $data);
+                    }
+                    if ($field) {
+                        $field->update_content($rid, $value, $name);
+                    }
                 }
             }
         }
 
         add_to_log($course->id, 'data', 'update', "view.php?d=$data->id&amp;rid=$rid", $data->id, $cm->id);
 
-        redirect($CFG->wwwroot.'/mod/data/view.php?d='.$data->id.'&rid='.$rid);
+        redirect($CFG->wwwroot.'/mod/data/view.php?d='.$data->id.'&rid='.($copyid ? $copyid : $rid));
 
     } else { /// Add some new records
 
diff --git a/mod/data/lib.php b/mod/data/lib.php
index a22aa6b..ed42ceb 100755
--- a/mod/data/lib.php
+++ b/mod/data/lib.php
@@ -782,7 +782,7 @@ function data_numentries($data){
  * @param int $groupid
  * @return bool
  */
-function data_add_record($data, $groupid=0){
+function data_add_record($data, $groupid=0, $replaces=0){
     global $USER, $DB;
 
     $cm = get_coursemodule_from_instance('data', $data->id);
@@ -798,6 +798,7 @@ function data_add_record($data, $groupid=0){
     } else {
         $record->approved = 0;
     }
+    $record->replaces = $replaces;
     return $DB->insert_record('data_records', $record);
 }
 
diff --git a/mod/data/version.php b/mod/data/version.php
index b57666a..0cc3c8c 100644
--- a/mod/data/version.php
+++ b/mod/data/version.php
@@ -5,7 +5,7 @@
 //  This fragment is called by /admin/index.php
 ////////////////////////////////////////////////////////////////////////////////
 
-$module->version  = 2010100101;
+$module->version  = 2010120100;
 $module->requires = 2010080300;  // Requires this Moodle version
 $module->cron     = 60;
 
diff --git a/mod/data/view.php b/mod/data/view.php
index 818bceb..25c2a8f 100755
--- a/mod/data/view.php
+++ b/mod/data/view.php
@@ -408,7 +408,24 @@ if ($showactivity) {
                     $newrecord = new stdClass();
                     $newrecord->id = $approverecord->id;
                     $newrecord->approved = 1;
+                    $newrecord->replaces = 0;
                     $DB->update_record('data_records', $newrecord);
+
+                    if ($approverecord->replaces) { // Delete the old record that this replaced
+                        if ($deleterecord = $DB->get_record('data_records', array('id'=>$approverecord->replaces))) {   // Need to check this is valid
+                            if ($deleterecord->dataid == $data->id) {                       // Must be from this database
+                                if ($contents = $DB->get_records('data_content', array('recordid'=>$deleterecord->id))) {
+                                    foreach ($contents as $content) {  // Delete files or whatever else this field allows
+                                        if ($field = data_get_field_from_id($content->fieldid, $data)) { // Might not be there
+                                            $field->delete_content($content->recordid);
+                                        }
+                                    }
+                                }
+                                $DB->delete_records('data_content', array('recordid'=>$deleterecord->id));
+                                $DB->delete_records('data_records', array('id'=>$deleterecord->id));
+                            }
+                        }
+                    }
                     echo $OUTPUT->notification(get_string('recordapproved','data'), 'notifysuccess');
                 }
             }
@@ -432,6 +449,7 @@ if ($showactivity) {
         }
 
     /// setup group and approve restrictions
+
         if (!$approvecap && $data->approval) {
             if (isloggedin()) {
                 $approveselect = ' AND (r.approved=1 OR r.userid=:myid1) ';
@@ -478,7 +496,7 @@ if ($showactivity) {
                     $ordering = "r.timecreated $order";
             }
 
-            $what = ' DISTINCT r.id, r.approved, r.timecreated, r.timemodified, r.userid, u.firstname, u.lastname';
+            $what = ' DISTINCT r.id, r.approved, r.timecreated, r.timemodified, r.userid, u.firstname, u.lastname, r.replaces';
             $count = ' COUNT(DISTINCT c.recordid) ';
             $tables = '{data_content} c,{data_records} r, {data_content} cs, {user} u ';
             $where =  'WHERE c.recordid = r.id
@@ -523,7 +541,7 @@ if ($showactivity) {
             $sortcontent = $DB->sql_compare_text('c.' . $sortfield->get_sort_field());
             $sortcontentfull = $sortfield->get_sort_sql($sortcontent);
 
-            $what = ' DISTINCT r.id, r.approved, r.timecreated, r.timemodified, r.userid, u.firstname, u.lastname, ' . $sortcontentfull . ' AS _order ';
+            $what = ' DISTINCT r.id, r.approved, r.timecreated, r.timemodified, r.userid, u.firstname, u.lastname, r.replaces, ' . $sortcontentfull . ' AS _order ';
             $count = ' COUNT(DISTINCT c.recordid) ';
             $tables = '{data_content} c, {data_records} r, {data_content} cs, {user} u ';
             $where =  'WHERE c.recordid = r.id
@@ -568,18 +586,21 @@ if ($showactivity) {
     /// To actually fetch the records
 
         $fromsql    = "FROM $tables $advtables $where $advwhere $groupselect $approveselect $searchselect $advsearchselect";
+        $replacessql = $fromsql.' AND r.replaces != 0';
         $sqlselect  = "SELECT $what $fromsql $sortorder";
         $sqlcount   = "SELECT $count $fromsql";   // Total number of records when searching
+        $replacecount = "SELECT $count $replacessql"; // Number of records that are replacing others (but not approved yet)
         $sqlmax     = "SELECT $count FROM $tables $where $groupselect $approveselect"; // number of all recoirds user may see
+        $replacesmax = "SELECT $count FROM $tables $where $groupselect $approveselect AND r.replaces != 0";
         $allparams  = array_merge($params, $advparams);
 
     /// Work out the paging numbers and counts
 
-        $totalcount = $DB->count_records_sql($sqlcount, $allparams);
+        $totalcount = $DB->count_records_sql($sqlcount, $allparams) - $DB->count_records_sql($replacecount, $allparams);
         if (empty($searchselect) && empty($advsearchselect)) {
             $maxcount = $totalcount;
         } else {
-            $maxcount = $DB->count_records_sql($sqlmax, $params);
+            $maxcount = $DB->count_records_sql($sqlmax, $params) - $DB->count_records_sql($replacesmax, $params);
         }
 
         if ($record) {     // We need to just show one, so where is it in context?
@@ -636,6 +657,14 @@ if ($showactivity) {
                 echo $OUTPUT->notification(get_string('foundrecords', 'data', $a), 'notifysuccess');
             }
 
+            foreach($records as $record) {
+                if ($record->replaces > 0) {
+                    if (isset($records[$record->replaces])) {
+                        unset($records[$record->replaces]);
+                    }
+                }
+            }
+
             if ($mode == 'single') {                  // Single template
                 $baseurl = 'view.php?d=' . $data->id . '&amp;mode=single&amp;';
                 if (!empty($search)) {
