3NF complete

Signed-off-by: Smita Kumari <kumarismita62@gmail.com>
This commit is contained in:
Smita Kumari 2014-07-30 16:20:38 +05:30
parent f6ea59fac3
commit d766b1459a
4 changed files with 410 additions and 21 deletions

View File

@ -326,6 +326,10 @@ $js_messages['strMoveRepeatingGroup'] = '<ol><b>' . __('The following actions wi
. '<li>' . __('Create the following table') . '</li>';
$js_messages['strNewTablePlaceholder'] = 'Enter new table name';
$js_messages['strNewColumnPlaceholder'] = 'Enter column name';
$js_messages['str3NFNormalization'] = __('Third step of normalization (3NF)');
$js_messages['strConfirmTd'] = __('Confirm transitive dependencies');
$js_messages['strSelectedTd'] = __('Selected dependencies are as follows:');
$js_messages['strNoTdSelected'] = __('No dependencies selected!');
/* For server_variables.js */
$js_messages['strSave'] = __('Save');

View File

@ -31,7 +31,39 @@ function appendHtmlColumnsList()
}
);
}
function goTo3NFStep1(newTables)
{
if (Object.keys(newTables).length === 1) {
newTables = [PMA_commonParams.get('table')];
}
$.post(
"normalization.php",
{
"token": PMA_commonParams.get('token'),
"ajax_request": true,
"db": PMA_commonParams.get('db'),
"tables": newTables,
"step": '3.1'
}, function(data) {
$("#page_content h3").html(PMA_messages.str3NFNormalization);
$("#mainContent legend").html(data.legendText);
$("#mainContent h4").html(data.headText);
$("#mainContent p").html(data.subText);
$("#mainContent #extra").html(data.extra);
$("#extra form").each(function() {
form_id = $(this).attr('id');
colname = $(this).data('colname');
$("#"+form_id+" input[value='"+colname+"']").next().remove();
$("#"+form_id+" input[value='"+colname+"']").remove();
});
$("#mainContent #newCols").html('');
$('.tblFooters').html('');
if (data.subText !== "") {
$('.tblFooters').html('<input type="button" onClick="processDependencies(\'\', true);" value="'+PMA_messages.strDone+'"/>');
}
}
);
}
function goTo2NFStep1() {
$.post(
"normalization.php",
@ -49,7 +81,14 @@ function goTo2NFStep1() {
$("#mainContent #extra").html(data.extra);
$("#mainContent #newCols").html('');
if (data.subText !== '') {
$('.tblFooters').html('<input type="submit" value="'+PMA_messages.strDone+'" onclick="processPartialDependancies(\''+data.primary_key+'\');">');
$('.tblFooters').html('<input type="submit" value="'+PMA_messages.strDone+'" onclick="processDependencies(\''+data.primary_key+'\');">');
} else {
if (normalizeto === '3nf') {
$("#mainContent #newCols").html(PMA_messages.strToNextStep);
setTimeout(function() {
goTo3NFStep1([PMA_commonParams.get('table')]);
}, 3000);
}
}
});
}
@ -154,6 +193,7 @@ function goToStep2(extra)
}
);
}
function goTo2NFFinish(pd)
{
var tables = {};
@ -167,6 +207,51 @@ function goTo2NFFinish(pd)
"pd": JSON.stringify(pd),
"newTablesName":JSON.stringify(tables),
"createNewTables2NF":1};
$.ajax({
type: "GET",
url: "normalization.php",
data: datastring,
async:false,
success: function(data) {
if (data.success === true) {
if(data.queryError === false) {
if (normalizeto === '3nf') {
$("#pma_navigation_reload").click();
goTo3NFStep1(tables);
return true;
}
$("#mainContent legend").html(data.legendText);
$("#mainContent h4").html(data.headText);
$("#mainContent p").html('');
$("#mainContent #extra").html('');
$('.tblFooters').html('');
} else {
PMA_ajaxShowMessage(data.extra, false);
}
$("#pma_navigation_reload").click();
} else {
PMA_ajaxShowMessage(data.error, false);
}
}
});
}
function goTo3NFFinish(newTables)
{
for (var table in newTables) {
for (var newtbl in newTables[table]) {
updatedname = $('#extra input[name="'+newtbl+'"]').val();
newTables[table][updatedname] = newTables[table][newtbl];
if (updatedname !== newtbl) {
delete newTables[table][newtbl];
}
}
}
datastring = {"token": PMA_commonParams.get('token'),
"ajax_request": true,
"db": PMA_commonParams.get('db'),
"newTables":JSON.stringify(newTables),
"createNewTables3NF":1};
$.ajax({
type: "GET",
url: "normalization.php",
@ -202,7 +287,7 @@ function goTo2NFStep2(pd, primary_key)
for (var dependson in pd) {
if (dependson !== primary_key) {
pdFound = true;
extra += '<p class="displayblock desc">'+dependson +" -> "+pd[dependson].toString()+'</p>';
extra += '<p class="displayblock desc">'+escapeHtml(dependson) +" -> "+escapeHtml(pd[dependson].toString())+'</p>';
}
}
if(!pdFound) {
@ -237,12 +322,74 @@ function goTo2NFStep2(pd, primary_key)
});
}
function processPartialDependancies(primary_key)
function goTo3NFStep2(pd, tablesTds)
{
$("#newCols").html('');
$("#mainContent legend").html(PMA_messages.strStep+' 3.2 '+PMA_messages.strConfirmTd);
$("#mainContent h4").html(PMA_messages.strSelectedTd);
$("#mainContent p").html(PMA_messages.strPdHintNote);
var extra = '<div class="dependencies_box">';
var pdFound = false;
for (var table in tablesTds) {
for (var i in tablesTds[table]) {
dependson = tablesTds[table][i];
if (dependson !== '' && dependson !== table) {
pdFound = true;
extra += '<p class="displayblock desc">'+escapeHtml(dependson) +" -> "+escapeHtml(pd[dependson].toString())+'</p>';
}
}
}
if(!pdFound) {
extra += '<p class="displayblock desc">'+PMA_messages.strNoTdSelected+'</p>';
extra += '</div>';
} else {
extra += '</div>';
datastring = {"token": PMA_commonParams.get('token'),
"ajax_request": true,
"db": PMA_commonParams.get('db'),
"tables": JSON.stringify(tablesTds),
"pd": JSON.stringify(pd),
"getNewTables3NF":1};
$.ajax({
type: "GET",
url: "normalization.php",
data: datastring,
async:false,
success: function(data) {
data_parsed = $.parseJSON(data.message);
if (data.success === true) {
extra += data_parsed.html;
} else {
PMA_ajaxShowMessage(data.error, false);
}
}
});
}
$("#mainContent #extra").html(extra);
$('.tblFooters').html('<input type="button" value="'+PMA_messages.strBack+'" id="backEditPd"/><input type="button" id="goTo3NFFinish" value="'+PMA_messages.strGo+'"/>');
$("#goTo3NFFinish").click(function(){
if (!pdFound) {
goTo3NFFinish([]);
} else {
goTo3NFFinish(data_parsed.newTables);
}
});
}
function processDependencies(primary_key, isTransitive)
{
var pd = {};
var tablesTds = {};
var dependsOn;
pd[primary_key] = [];
$("#extra form").each(function() {
if (isTransitive === true) {
tblname = $(this).data('tablename');
primary_key = tblname;
if (!(tblname in tablesTds)) {
tablesTds[tblname] = [];
}
tablesTds[tblname].push(primary_key);
}
form_id = $(this).attr('id');
$('#'+form_id+' input[type=checkbox]:not(:checked)').removeAttr('checked');
dependsOn = '';
@ -251,9 +398,7 @@ function processPartialDependancies(primary_key)
$(this).attr("checked","checked");
});
if (dependsOn === '') {
$('#'+form_id+' input[type=checkbox]').each(function(){
dependsOn = primary_key;
});
dependsOn = primary_key;
} else {
dependsOn = dependsOn.slice(0, -2);
}
@ -261,11 +406,24 @@ function processPartialDependancies(primary_key)
pd[dependsOn] = [];
}
pd[dependsOn].push($(this).data('colname'));
if (isTransitive === true) {
if (!(tblname in tablesTds)) {
tablesTds[tblname] = [];
}
if ($.inArray(dependsOn, tablesTds[tblname]) === -1) {
tablesTds[tblname].push(dependsOn);
}
}
});
backup = $("#mainContent").html();
goTo2NFStep2(pd, primary_key);
if (isTransitive === true) {
goTo3NFStep2(pd, tablesTds);
} else {
goTo2NFStep2(pd, primary_key);
}
return false;
}
function moveRepeatingGroup(repeatingCols) {
newTable = $("input[name=repeatGroupTable]").val();
newColumn = $("input[name=repeatGroupColumn]").val();

View File

@ -337,8 +337,9 @@ function PMA_getHtmlFor2NFstep1($db, $table)
foreach ($columns as $column) {
if (!in_array($column, $pk)) {
$cnt++;
$extra .= "<b>'" . htmlspecialchars($column)
. "' depends on:</b><br>";
$extra .= "<b>" . sprintf(
__('\'%1$s\' depends on:'), htmlspecialchars($column)
) . "</b><br>";
$extra .= '<form id="pk_' . $cnt . '" data-colname="'
. htmlspecialchars($column) . '" class="smallIndent">'
. $selectPkForm . '</form><br/><br/>';
@ -376,7 +377,7 @@ function PMA_getHtmlForNewTables2NF($partialDependencies,$table)
'As per above partial dependencies, in order to put the '
. 'original table \'%1$s\' into Second normal form we need '
. 'to create the following tables:'
), $table
), htmlspecialchars($table)
) . '</b></p>';
$tableName = $table;
$i=1;
@ -466,6 +467,148 @@ function PMA_createNewTablesFor2NF($partialDependencies, $tablesName, $table, $d
);
}
/**
* build the html for showing the new tables to have in order
* to put given tables in 3NF
*
* @param array $dependencies containing all the dependencies
* @param array $tables tables formed after 2NF and need to convert to 3NF
* @param string $db current database
*
* @return array containing html and the list of new tables
*/
function PMA_getHtmlForNewTables3NF($dependencies, $tables, $db)
{
$html = "";
$i=1;
$newTables = array();
foreach ($tables as $table=>$arrDependson) {
if (count(array_unique($arrDependson)) == 1) {
continue;
}
$primary = PMA_Index::getPrimary($table, $db);
$primarycols = $primary->getColumns();
$pk = array();
foreach ($primarycols as $col) {
$pk[] = $col->getName();
}
$html .= '<p><b>' . sprintf(
__(
'As per above dependencies, in order to put the '
. 'original table \'%1$s\' into Third normal form we need '
. 'to create the following tables:'
), htmlspecialchars($table)
) . '</b></p>';
$tableName = $table;
$columnList = array();
foreach ($arrDependson as $key) {
$dependents = $dependencies->$key;
if ($key == $table) {
$key = implode(', ', $pk);
}
$tmpTableCols =array_merge(explode(', ', $key), $dependents);
sort($tmpTableCols);
if (!in_array($tmpTableCols, $columnList)) {
$columnList[] = $tmpTableCols;
$html .= '<p><input type="text" name="'
. htmlspecialchars($tableName)
. '" value="' . htmlspecialchars($tableName) . '"/>'
. '( <u>' . htmlspecialchars($key) . '</u>'
. (count($dependents)>0?', ':'')
. htmlspecialchars(implode(', ', $dependents)) . ' )';
$newTables[$table][$tableName] = array(
"pk"=>$key, "nonpk"=>implode(', ', $dependents)
);
$i++;
$tableName = 'table' . $i;
}
}
}
return array('html'=>$html, 'newTables'=>$newTables);
}
/**
* create new tables or alter existing to get 3NF
*
* @param array $newTables list of new tables to be created
* @param string $db current database
*
* @return array
*/
function PMA_createNewTablesFor3NF($newTables, $db)
{
$queries = array();
$dropCols = false;
$error = false;
$headText = '<h3>' .
__('The third step of normalization is complete.')
. '</h3>';
if (count((array)$newTables) == 0) {
return array(
'legendText'=>__('End of step'), 'headText'=>$headText,
'queryError'=>$error
);
}
$message = '';
$GLOBALS['dbi']->selectDb($db, $GLOBALS['userlink']);
foreach ($newTables as $originalTable=>$tablesList) {
foreach ($tablesList as $table=>$cols) {
if ($table != $originalTable) {
$quotedPk = implode(
', ', PMA_Util::backquote(explode(', ', $cols->pk))
);
$quotedNonpk = implode(
', ', PMA_Util::backquote(explode(', ', $cols->nonpk))
);
$queries[] = 'CREATE TABLE ' . PMA_Util::backquote($table)
. ' SELECT DISTINCT ' . $quotedPk
. ', ' . $quotedNonpk
. ' FROM ' . PMA_Util::backquote($originalTable) . ';';
$queries[] = 'ALTER TABLE ' . PMA_Util::backquote($table)
. ' ADD PRIMARY KEY(' . $quotedPk . ');';
} else {
$dropCols = $cols;
}
}
if ($dropCols) {
$columns = (array) $GLOBALS['dbi']->getColumnNames(
$db, $originalTable, $GLOBALS['userlink']
);
$colPresent = array_merge(
explode(', ', $dropCols->pk), explode(', ', $dropCols->nonpk)
);
$query = 'ALTER TABLE ' . PMA_Util::backquote($originalTable);
foreach ($columns as $col) {
if (!in_array($col, $colPresent)) {
$query .= ' DROP ' . PMA_Util::backquote($col) . ',';
}
}
$query = trim($query, ', ');
$query .= ';';
$queries[] = $query;
} else {
$queries[] = 'DROP TABLE ' . PMA_Util::backquote($originalTable);
}
$dropCols = false;
}
foreach ($queries as $query) {
if (!$GLOBALS['dbi']->tryQuery($query, $GLOBALS['userlink'])) {
$message = PMA_Message::error(__('Error in processing!'));
$message->addMessage('<br /><br />');
$message->addMessage(
PMA_Message::rawError(
$GLOBALS['dbi']->getError($GLOBALS['userlink'])
)
);
$error = true;
break;
}
}
return array(
'legendText'=>__('End of step'), 'headText'=>$headText,
'queryError'=>$error, 'extra'=>$message
);
}
/**
* move the repeating group of columns to a new table
*
@ -528,6 +671,83 @@ function PMA_moveRepeatingGroup(
'queryError'=>$error, 'message'=>$message
);
}
/**
* build html for 3NF step 1 to find the transitive dependencies
*
* @param string $db current database
* @param array $tables tables formed after 2NF and need to process for 3NF
*
* @return string
*/
function PMA_getHtmlFor3NFstep1($db, $tables)
{
$legendText = __('Step 3.') . "1 " . __('Find transitive dependencies');
$extra = "";
$headText = __(
'Please answer the following question(s) '
. 'carefully to obtain a correct normalization.'
);
$subText = __(
'For each of the column below, '
. 'please select the <b>minimal set</b> of columns among given set '
. 'whose values combined together are sufficient'
. ' to determine the value of the column.<br>'
. 'Note: A column may have no transitive dependency, '
. 'in that case you don\'t have to select any.'
);
$cnt=0;
foreach ($tables as $key=>$table) {
$primary = PMA_Index::getPrimary($table, $db);
$primarycols = $primary->getColumns();
$selectTdForm = "";
$pk = array();
foreach ($primarycols as $col) {
$pk[] = $col->getName();
}
$GLOBALS['dbi']->selectDb($db, $GLOBALS['userlink']);
$columns = (array) $GLOBALS['dbi']->getColumnNames(
$db, $table, $GLOBALS['userlink']
);
if (count($columns)-count($pk)<=1) {
continue;
}
foreach ($columns as $column) {
if (!in_array($column, $pk)) {
$selectTdForm .= '<input type="checkbox" name="pd" value="'
. htmlspecialchars($column) . '">'
. '<span>' . htmlspecialchars($column) . '</span>';
}
}
foreach ($columns as $column) {
if (!in_array($column, $pk)) {
$cnt++;
$extra .= "<b>" . sprintf(
__('\'%1$s\' depends on:'), htmlspecialchars($column)
)
. "</b><br>";
$extra .= '<form id="td_' . $cnt . '" data-colname="'
. htmlspecialchars($column) . '" data-tablename="'
. htmlspecialchars($table) . '" class="smallIndent">'
. $selectTdForm
. '</form><br/><br/>';
}
}
}
if ($extra == "") {
$headText = __(
"No Transitive dependencies possible as the table "
. "doesn't have any non primary key columns"
);
$subText = "";
$extra = "<h3>" . __("Table is already in Third normal form!") . "</h3>";
}
$res = array(
'legendText'=>$legendText, 'headText'=>$headText,
'subText'=>$subText,'extra'=>$extra
);
return $res;
}
/**
* get html for options to normalize table
*

View File

@ -57,6 +57,13 @@ if (isset($_REQUEST['getNewTables2NF'])) {
exit;
}
if (isset($_REQUEST['getNewTables3NF'])) {
$dependencies = json_decode($_REQUEST['pd']);
$tables = json_decode($_REQUEST['tables']);
$newTables = PMA_getHtmlForNewTables3NF($dependencies, $tables, $db);
echo json_encode($newTables);
exit;
}
$response = PMA_Response::getInstance();
$header = $response->getHeader();
@ -66,16 +73,6 @@ $scripts->addFile('jquery/jquery.uitablefilter.js');
$normalForm = '1nf';
if (isset($_REQUEST['normalizeTo'])) {
$normalForm = $_REQUEST['normalizeTo'];
if ($normalForm == '3nf') {
$response->addHTML(
'<h3 class="center">'
. __('Second/Third step of normalization') . '</h3>'
. '<fieldset>'
. '<legend>Coming soon...</legend>'
. 'Wait is worth it :-)</fieldset>'
);
exit;
}
}
if (isset($_REQUEST['createNewTables2NF'])) {
$partialDependencies = json_decode($_REQUEST['pd']);
@ -84,6 +81,12 @@ if (isset($_REQUEST['createNewTables2NF'])) {
$response->addJSON($res);
exit;
}
if (isset($_REQUEST['createNewTables3NF'])) {
$newtables = json_decode($_REQUEST['newTables']);
$res = PMA_createNewTablesFor3NF($newtables, $db);
$response->addJSON($res);
exit;
}
if (isset($_POST['repeatingColumns'])) {
$repeatingColumns = $_POST['repeatingColumns'];
$newTable = $_POST['newTable'];
@ -110,6 +113,10 @@ if (isset($_REQUEST['step1'])) {
} else if (isset($_REQUEST['step']) && $_REQUEST['step'] == 2.1) {
$res = PMA_getHtmlFor2NFstep1($db, $table);
$response->addJSON($res);
} else if (isset($_REQUEST['step']) && $_REQUEST['step'] == 3.1) {
$tables = $_REQUEST['tables'];
$res = PMA_getHtmlFor3NFstep1($db, $tables);
$response->addJSON($res);
} else {
$response->addHTML(PMA_getHtmlForNormalizetable());
}