/*********************************************************************************** * @author John Miller * @version 1.0 * @date Tue Jan 19 15:51:41 EST 2010 */ /*********************************************************************************** * Object containing functions for creating Boyce-Codd Normal Form (BCNF) * decompositions. * Notes: the algorithm is incomplete in that it only considers F, not F+. * requires scala 2.8 (http://www.scala-lang.org/downloads) nightly build */ object BCNFdecomp extends Application { type AttrSet = Set [Char] type Dependency = Tuple2 [AttrSet, AttrSet] // (lhs, rhs) /******************************************************************************* * Compute the closure of a given set of attributes. * @param x the set of attribute to take the closure of * @param fd the list of functional dependencies * @return the closure of x (x+) */ def closure (x: AttrSet, fd: List [Dependency]): AttrSet = { var z = x var changes = true while (changes) { changes = false for (f <- fd if (f._1 subsetOf z) && ! (f._2 subsetOf z)) { z ++= f._2 changes = true } // for } // while z } // closure /******************************************************************************* * Using F, check whether the candidate subschema is in BCNF, if not split * it into two subschemas. * @param ri the candidate subschema * @param fd the list of functional dependencies * @return either ri or ri split into two subschemas */ def split (ri: AttrSet, fd: List [Dependency]): List [AttrSet] = { for (f <- fd if f._1 subsetOf ri) { // relevant fd? lhs subset-of ri val clo = closure (f._1, fd) // fd must be non-trivial and lhs must not be a superkey if ( ! ((f._2 & ri) subsetOf f._1) && ! (ri subsetOf clo) ) { return List (f._1 ++ (f._2 & ri), ri -- f._2) } // if } // for List (ri) // ri is in BCNF, so no split is necessary } // split /******************************************************************************* * Decompose a schema into BCNF (note, only considers F, not F+). * @param r the initial schema to decompose * @param fd the list of functional dependencies * @return the BCNF decomposition of r */ def decomp (r: AttrSet, fd: List [Dependency]): List [AttrSet] = { var rhoIn: List [AttrSet] = List (r) // input list of subschemas var rhoCan: List [AttrSet] = List () // candidate list var rhoOut: List [AttrSet] = List () // final output list while (! rhoIn.isEmpty) { println ("while:\trhoIn = " + rhoIn + "\n\trhoOut = " + rhoOut) for (ri <- rhoIn) { val r1_2 = split (ri, fd) if (r1_2.length == 1) rhoOut ++= r1_2 else rhoCan ++= r1_2 println ("\tfor: ri = " + ri + "\n\t\t\trhoIn = " + rhoIn + "\n\t\t\trhoCan = " + rhoCan + "\n\t\t\trhoOut = " + rhoOut) } // for rhoIn = rhoCan // on next iteration, decompose using candidates rhoCan = List () // start a new empty candidate list } // while rhoOut } // decomp /******************************************************************************* * Determine whether a dependency is preserved in the decomposition. * @param f the dependency to check * @param rho the decomposition (list of subschema) * @param fd the list of functional dependencies * @return whether the depedency is preserved */ def isPreserved (f: Dependency, rho: List [AttrSet], fd: List [Dependency]): Boolean = { var z = f._1 var changes = true while (changes) { changes = false for (ri <- rho) { val zz = closure (z & ri, fd) & ri if (! (zz subsetOf z)) { z ++= zz; changes = true} } // for } // while f._2 subsetOf z } // isPreserved /******************************************************************************* * Use the BCNF decomposition algorithm to produce a database design rho * (a list of subschemas) and check rho for dependency preservation. */ def designBCNF (r: AttrSet, fd: List [Dependency]) { println ("\n------------------------------------") println ("Test of BCNF Decomposition Algorithm") println ("------------------------------------") println ("r = " + r) fd.foreach ( (f) => println ("fd: " + f._1 + " --> " + f._2) ) println ("-------------------------------------------") println ("Decompose based on the original set of fd's") println ("-------------------------------------------") val rho = decomp (r, fd) println ("\ndecomposition: rho = " + rho + "\n") for (f <- fd) println ("fd: " + f._1 + " --> " + f._2 + " is preserved?\t= " + isPreserved (f, rho, fd)) } // designBCNF /******************************************************************************* * Test the BCNF Decompostion Algorithm. */ val r1 = Set ('C', 'G', 'H', 'R', 'S', 'T') val fd1 = List ( (Set ('C'), Set ('T')), // C --> T (Set ('C', 'S'), Set ('G')), // CS --> G (Set ('H', 'R'), Set ('C')), // HR --> C (Set ('H', 'S'), Set ('R')), // HS --> R (Set ('H', 'T'), Set ('R')) ) // HT --> R designBCNF (r1, fd1) val r2 = Set ('A', 'B', 'C', 'D', 'E') val fd2 = List ( (Set ('A'), Set ('C')), // A --> C (Set ('C'), Set ('D', 'E')) ) // C --> DE designBCNF (r2, fd2) } // BCNFdecomp