Rollback Backup Data Secara Manual

Tuesday, February 16, 2016

Merujuk kepada post sebelum ini, terdapat dua table. Satu untuk data original, kedua adalah data backup. Di mana table backup tersebut berjalan menggunakan TRIGGER.

Table Original
mysql> select * from data;
+----+---------------------+-----------------------+-------+
| id | timestamp           | data1                 | data2 |
+----+---------------------+-----------------------+-------+
|  1 | 2016-02-15 23:11:34 | masukkan satu manual  |  2.44 |
|  2 | 2016-02-15 14:40:22 | data kedua berubah    |  0.00 |
|  3 | 2016-02-15 13:58:08 | data pertama          |  2.20 |
|  4 | 2016-02-15 13:58:08 | data kedua            |  4.50 |
|  5 | 2016-02-15 23:12:09 | ini baris yang kelima |  5.40 |
+----+---------------------+-----------------------+-------+
 
Table Backup menggunakan trigger
mysql> select * from data_log;
+-------+--------+----+---------------------+-------------------------+-------+
| logid | action | id | timestamp           | data1                   | data2 |
+-------+--------+----+---------------------+-------------------------+-------+
|     1 | I      |  1 | 2016-02-15 14:03:46 | data pertama edit       |  0.00 |
|     2 | I      |  2 | 2016-02-15 13:57:21 | data kedua              |  0.00 |
|     3 | I      |  3 | 2016-02-15 13:58:08 | data pertama            |  2.20 |
|     4 | I      |  4 | 2016-02-15 13:58:08 | data kedua              |  4.50 |
|     5 | U      |  1 | 2016-02-15 14:06:44 | data pertama tidak edit |  2.00 |
|     6 | D      |  1 | 2016-02-15 14:07:43 | data pertama tidak edit |  2.00 |
|     7 | U      |  2 | 2016-02-15 14:40:22 | data kedua berubah      |  0.00 |
|     8 | I      |  1 | 2016-02-15 23:11:34 | masukkan satu manual    |  2.44 |
|     9 | I      |  5 | 2016-02-15 23:12:09 | ini baris yang kelima   |  5.40 |
+-------+--------+----+---------------------+-------------------------+-------+


Sekarang kita mahu membuat rollback (memberikan semula data yang telah diubah kepada table original) untuk satu data.

Kita ambil contoh, table: data, id:1, data1: "masukkan satu manual". Data ini mahu ditukar ke data sebelumnya.

Data yang dirujuk untuk aktiviti rollback adalah, table:data_log, logid:6, id:1, data1:"data pertama tidak edit". Kita akan pulangkan semula data ini ke table:data.

Biasanya, kita boleh membuat rollback dengan satu arahan SQL sahaja. Sebagai contoh,
/* Contoh #1 */
UPDATE data SET 
  timestamp = (SELECT timestamp FROM data_log WHERE logid='6' AND data.id=data_log.id),
  data1=(SELECT data1 FROM data_log WHERE logid='6' AND data.id=data_log.id),
  data2=(SELECT data2 FROM data_log WHERE logid='6' AND data.id=data_log.id);

/* Contoh #2 */
UPDATE data a
SET (a.timestamp, a.data1, a.data2) = (SELECT b.timestamp, b.data1, b.data2 
 FROM data_log b 
WHERE b.id = a.id AND b.logid='6');

/* Contoh #3 */
UPDATE data_log
INNER JOIN
data
ON data.id = data_log.id
SET data.timestamp = data_log.timestamp, 
 data.data1 = data_log.data1,
 data.data2 = data_log.data2
WHERE data_log.logid='6'

/* dan pelbagai cara lagi */

Tetapi dalam situasi ini, ia tidak dibenarkan. SQL akan error. Kerana table:data_log merupakan table yang sentiasa di INSERT oleh TRIGGER, manakala table:data sentiasa dikawal oleh TRIGGER.

Apabila kedua-dua table ini diakses dengan serentak, malah mahu mengemaskini juga dalam masa yang sama, SQL tidak benarkan perkara ini berlaku. SELECT + COMPARE + UPDATE dalam satu arahan.

Apa yang boleh dibantu adalah dengan membuat programming. Contoh di sini kita menggunakan PHP.

Apa yang akan kita buat adalah:
  1. Dapatkan nama field yang mahu diupdate dari table data_log
  2. Sediakan SQL query untuk UPDATE table data
  3. Dapatkan nilai setiap data terlibat pada table data_log guna logid=6
  4. Cantumkan dapatan di langkah (1) dan (3) ke dalam SQL query di langkah (2) menjadi SQL yang lengkap.
  5. Jalankan SQL query tersebut untuk membuat kemaskini.
Secara programmingnya begini,

<?php 

$logid = $_GET['logid']; //output = 6

//1. Dapatkan nama field. Limit =1 bertujuan untuk ambil data terkecil, query tidak makan masa.
$result = mysql_query("SELECT * FROM data_log limit 1");
if (!$result) {
    die("Query to show fields from table failed");
}
//   Jumlah field dari arahan di atas.
//   fields_num = 5 
$fields_num = mysql_num_fields($result);

//   Sekadar nak lihat nama field.
//   Bermula dengan nilai $i=3, kerana:
//   0 = running ID log 
//   1 = Activity log 
//   2 = ID for original table
//   3 ~ n = data yang dimahukan.
for($i=3;  $i<$fields_num; $i++)
{
    $field = mysql_fetch_field($result, $i);
    echo "{$field->name}".$i."<br>";    
}
//   Di sini kita menggunakan mysql_fetch_field($result, $i)
//   Supaya namafield yang dipaparkan bermula dengan field data-data.
//   Jika sekadar guna mysql_fetch_field($result) tanpa $i, 
//   Ia akan memaparkan field bermula dari 0
//   Walaupun telah diberitahu, for($i=3..)

//2. SQL query untuk UPDATE table:data
$sql_rollback = "UPDATE data SET ";

//3. Dapatkan nilai setiap data yang terlibat.
//   masukkan ke dalam row2[];
$result2 = mysql_query("SELECT * FROM data_log WHERE logid = '$logid'");
if (!$result2) {
    echo 'Could not run query: ' . mysql_error();
    exit;
}
$row2 = mysql_fetch_row($result2);


//4. Cantumkan dapatan di atas menjadi SQL yang lengkap.
//   Setiap nama field ({$field->name}) dan data ($row[]) digabungkan
//   Pastikan tiada tanda ',' pada nama field yang terakhir. Guna if($i == ($fields_num - 1))
for($i=3; $i<$fields_num; $i++)
{
    $field = mysql_fetch_field($result, $i);
 if($i == ($fields_num - 1)){
  $sql_rollback .= "{$field->name} = '".$row2[$i]."'";
 } else {
  $sql_rollback .= "{$field->name} = '".$row2[$i]."', ";
 }
}

//   Dapatkan field ID untuk table:data.
$field_id = mysql_fetch_field($result, 2);

//   Lengkapkan SQL Query
//   Gunakan field ID tadi untuk WHERE
//   Dan running SQL Query guna mysql_query()
$sql_rollback .=" WHERE {$field_id->name} = '".$row2[2]."'";
$resultrb = mysql_query($sql_rollback);
?>

Lihat hasilnya di database. Data telah dikemaskini, dan data_log juga sudah bertambah satu lagi baris data.

0 comments: