A full-stack web application that computes Candidate Keys and Superkeys from a given relation and its Functional Dependencies β a core problem in Relational Database Theory.
In RDBMS design, identifying candidate keys is fundamental for:
- Defining primary keys and ensuring data integrity
- Achieving proper normalization (2NF, 3NF, BCNF)
- Eliminating redundancy and update anomalies
Given a relation R(A, B, C, β¦) and a set of functional dependencies F, the tool answers:
| Question | Output |
|---|---|
| What are the minimal superkeys (candidate keys)? | Always computed |
| What are all superkeys? | Computed only when |R| β€ 8 (to avoid 2βΏ explosion) |
Example β R(A, B, C, D), F = {A β B, B β C, AC β D}:
Candidate Keys : {A}
Superkeys : {A}, {A,B}, {A,C}, {A,D}, {A,B,C}, {A,B,D}, {A,C,D}, {A,B,C,D}
- Java 17+ installed (
java -versionto verify) - No Maven installation needed (wrapper included)
- Clone the repository:
git clone https://github.com/Adarsh-Chaubey03/Key-Analyzer-RDBMS.git
cd "Key Analyzer β RDBMS"- Start the server:
java "-Dmaven.multiModuleProjectDirectory=." -classpath ".mvn\wrapper\maven-wrapper.jar" org.apache.maven.wrapper.MavenWrapperMain spring-boot:run- Open your browser: http://localhost:8080
The first run will download dependencies (~30s). Subsequent runs start in ~2 seconds.
| Layer | Technology |
|---|---|
| Backend | Java 17+ Β· Spring Boot 3.2 |
| Frontend | HTML5 Β· CSS3 Β· Vanilla JavaScript |
| Communication | REST API (JSON) |
| Build | Maven 3.9 (via wrapper) |
| Server | Embedded Apache Tomcat |
Key Analyzer β RDBMS/
βββ pom.xml # Maven config
βββ mvnw.cmd # Maven wrapper (Windows)
βββ .mvn/wrapper/ # Wrapper JARs
βββ src/main/
βββ java/com/keyanalyzer/
β βββ KeyAnalyzerApplication.java # Spring Boot entry point
β βββ config/
β β βββ WebConfig.java # CORS configuration
β βββ controller/
β β βββ KeyController.java # REST endpoint + timeout guard
β βββ core/
β β βββ KeyAnalyzer.java # Pure algorithm (no Spring deps)
β βββ model/
β β βββ FunctionalDependency.java
β β βββ KeyRequest.java # Input DTO
β β βββ KeyResponse.java # Output DTO
β βββ service/
β βββ KeyService.java # Validation + orchestration
βββ resources/
βββ application.properties
βββ static/
βββ index.html
βββ css/style.css # Dark terminal theme
βββ js/app.js # FD parser + API client
flowchart TD
A["User enters Attributes & FDs"] --> B["Frontend parses text β JSON"]
B --> C["POST /api/compute-keys"]
C --> D["Validate input"]
D -->|Invalid| E["Return 400 error"]
D -->|Valid| F["Compute Attribute Closures"]
F --> G["BFS Candidate Key Search"]
G --> H{"|R| β€ 8 ?"}
H -->|Yes| I["Enumerate all Superkeys"]
H -->|No| J["Skip Superkeys"]
I --> K["Return JSON Response"]
J --> K
K --> L["Frontend renders results"]
Computes all attributes functionally determined by a set X under F.
CLOSURE(X, F):
result = X
repeat
for each FD (Ξ± β Ξ²) in F:
if Ξ± β result:
result = result βͺ Ξ²
until result does not change
return result
Avoids brute-force power-set enumeration using two optimizations:
FIND-CANDIDATE-KEYS(R, F):
1. Partition attributes:
ESSENTIAL = attributes that NEVER appear on any RHS (must be in every key)
NON-ESSENTIAL = all other attributes
2. If CLOSURE(ESSENTIAL, F) = R β return {ESSENTIAL}
3. BFS β expand ESSENTIAL by adding one non-essential attribute at a time:
Queue β { ESSENTIAL βͺ {x} | x β NON-ESSENTIAL }
while Queue is not empty:
current = dequeue
if current is a SUPERSET of any known candidate key β PRUNE
if CLOSURE(current, F) = R β save as candidate key (don't expand further)
else β enqueue { current βͺ {y} | y comes after max(current) }
4. Return all candidate keys sorted by size
- |R| β€ 8 β Enumerate all 2βΏ β 1 non-empty subsets, check each via attribute closure
- |R| > 8 β Skip with message (exponential growth)
| Structure | Usage |
|---|---|
Set<String> (LinkedHashSet) |
Attribute sets β preserves insertion order, O(1) lookup for closure |
List<FunctionalDependency> |
Ordered collection of FDs for iterative closure computation |
Queue<Set<String>> (LinkedList) |
BFS frontier for level-wise candidate key expansion |
Set<String> (HashSet) |
Visited-set for BFS β canonical string keys prevent duplicate exploration |
List<Set<String>> |
Accumulator for discovered candidate keys and superkeys |
Bitmask (int) |
Superkey enumeration β each bit represents an attribute (β€ 8 attrs) |
Request:
{
"attributes": ["A", "B", "C", "D"],
"fds": [
{ "left": ["A"], "right": ["B"] },
{ "left": ["B"], "right": ["C"] },
{ "left": ["A", "C"], "right": ["D"] }
]
}Response:
{
"candidateKeys": [["A"]],
"superkeys": [["A"], ["A","B"], ["A","C"], ["A","D"], ...],
"info": {
"attributeCount": 4,
"strategy": "Optimized BFS with RHS-reduction pruning",
"executionTimeMs": 3,
"stepsCount": 26
}
}| Status | Condition |
|---|---|
200 |
Success |
400 |
Invalid input (missing attributes, unknown attribute in FD) |
408 |
Computation timed out (> 10 seconds) |
| Attributes | Candidate Keys | Superkeys | Behavior |
|---|---|---|---|
| β€ 8 | β Computed | β Computed | Full analysis |
| 9 β 15 | β Computed | CK via pruned BFS, superkeys too expensive | |
| > 15 | β Computed | 10s timeout guard prevents freeze |
This project is licensed under the MIT License.