dbis-functional-dependencies 0.0.7__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dbis_functional_dependencies/BCNF.py +1761 -0
- dbis_functional_dependencies/fdcheck.py +113 -0
- {functional_dependencies → dbis_functional_dependencies}/fds.py +14 -10
- dbis_functional_dependencies/fdsbase.py +207 -0
- dbis_functional_dependencies-1.0.0.dist-info/METADATA +178 -0
- dbis_functional_dependencies-1.0.0.dist-info/RECORD +10 -0
- {dbis_functional_dependencies-0.0.7.dist-info → dbis_functional_dependencies-1.0.0.dist-info}/WHEEL +1 -1
- dbis_functional_dependencies-1.0.0.dist-info/top_level.txt +1 -0
- dbis_functional_dependencies-0.0.7.dist-info/METADATA +0 -23
- dbis_functional_dependencies-0.0.7.dist-info/RECORD +0 -10
- dbis_functional_dependencies-0.0.7.dist-info/top_level.txt +0 -1
- functional_dependencies/BCNF.py +0 -619
- functional_dependencies/fdcheck.py +0 -96
- functional_dependencies/fdsbase.py +0 -194
- {functional_dependencies → dbis_functional_dependencies}/__init__.py +0 -0
- {dbis_functional_dependencies-0.0.7.dist-info → dbis_functional_dependencies-1.0.0.dist-info}/LICENSE +0 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
"""
|
2
|
+
Created on 2022-06-11
|
3
|
+
@author: wf
|
4
|
+
"""
|
5
|
+
import time
|
6
|
+
from dbis_functional_dependencies.BCNF import FunctionalDependencySet
|
7
|
+
from lodstorage.sql import SQLDB
|
8
|
+
|
9
|
+
|
10
|
+
class FDCheck:
|
11
|
+
"""
|
12
|
+
check functional dependencies for a tabular dataset in list of dicts form
|
13
|
+
"""
|
14
|
+
|
15
|
+
def __init__(self, lod: list, debug: bool = False):
|
16
|
+
"""
|
17
|
+
construct me with the given list of dicts
|
18
|
+
|
19
|
+
Args:
|
20
|
+
lod(list): the list of dicts (table) to check
|
21
|
+
debug(bool): if true switch on debugging
|
22
|
+
"""
|
23
|
+
self.lod = lod
|
24
|
+
self.debug = debug
|
25
|
+
self.entityInfo = None
|
26
|
+
|
27
|
+
def createDatabase(
|
28
|
+
self,
|
29
|
+
entityName,
|
30
|
+
primaryKey=None,
|
31
|
+
executeMany=True,
|
32
|
+
fixNone=False,
|
33
|
+
fixDates=False,
|
34
|
+
debug=False,
|
35
|
+
doClose=True,
|
36
|
+
):
|
37
|
+
"""
|
38
|
+
create a database for my list of Records
|
39
|
+
|
40
|
+
Args:
|
41
|
+
entityName(string): the name of the entity type to be used as a table name
|
42
|
+
primaryKey(string): the name of the key / column to be used as a primary key
|
43
|
+
executeMany(boolean): True if executeMany mode of sqlite3 should be used
|
44
|
+
fixNone(boolean): fix dict entries that are undefined to have a "None" entry
|
45
|
+
debug(boolean): True if debug information e.g. CREATE TABLE and INSERT INTO commands should be shown
|
46
|
+
doClose(boolean): True if the connection should be closed
|
47
|
+
|
48
|
+
"""
|
49
|
+
size = len(self.lod)
|
50
|
+
if self.debug:
|
51
|
+
print(
|
52
|
+
"%s size is %d fixNone is %r fixDates is: %r"
|
53
|
+
% (entityName, size, fixNone, fixDates)
|
54
|
+
)
|
55
|
+
self.sqlDB = SQLDB(debug=debug, errorDebug=True)
|
56
|
+
entityInfo = self.sqlDB.createTable(self.lod, entityName, primaryKey)
|
57
|
+
startTime = time.time()
|
58
|
+
self.sqlDB.store(self.lod, entityInfo, executeMany=executeMany, fixNone=fixNone)
|
59
|
+
elapsed = (
|
60
|
+
0.000000000001 if time.time() - startTime == 0 else time.time() - startTime
|
61
|
+
)
|
62
|
+
if self.debug:
|
63
|
+
print(
|
64
|
+
"adding %d %s records took %5.3f s => %5.f records/s"
|
65
|
+
% (size, entityName, elapsed, size / elapsed)
|
66
|
+
)
|
67
|
+
if self.debug:
|
68
|
+
resultList = self.sqlDB.queryAll(entityInfo, fixDates=fixDates)
|
69
|
+
print(
|
70
|
+
"selecting %d %s records took %5.3f s => %5.f records/s"
|
71
|
+
% (len(resultList), entityName, elapsed, len(resultList) / elapsed)
|
72
|
+
)
|
73
|
+
if doClose:
|
74
|
+
self.sqlDB.close()
|
75
|
+
self.entityInfo = entityInfo
|
76
|
+
return entityInfo
|
77
|
+
|
78
|
+
def findFDs(self):
|
79
|
+
"""
|
80
|
+
find functional dependencies
|
81
|
+
|
82
|
+
https://github.com/gustavclausen/functional-dependency-finder/blob/master/main.py
|
83
|
+
Return:
|
84
|
+
FunctionalDependencySet: the set of functional dependencies
|
85
|
+
"""
|
86
|
+
if self.entityInfo is None:
|
87
|
+
raise Exception("createDataBase needed to supply entityInfo")
|
88
|
+
fields = list(self.entityInfo.typeMap.keys())
|
89
|
+
table_name = self.entityInfo.name
|
90
|
+
fds = FunctionalDependencySet()
|
91
|
+
for i, field in enumerate(fields):
|
92
|
+
attr1_var = chr(ord("A") + i)
|
93
|
+
fds.add_attribute(attr1_var, field)
|
94
|
+
for i, field in enumerate(fields):
|
95
|
+
attr1_var = chr(ord("A") + i)
|
96
|
+
for j in range(0, len(fields)):
|
97
|
+
if i == j:
|
98
|
+
continue
|
99
|
+
|
100
|
+
field_1 = fields[i]
|
101
|
+
field_2 = fields[j]
|
102
|
+
attr2_var = chr(ord("A") + j)
|
103
|
+
sql = f"SELECT {field_1}, COUNT(DISTINCT {field_2}) c FROM {table_name} GROUP BY {field_1} HAVING c > 1"
|
104
|
+
hits = self.sqlDB.query(sql)
|
105
|
+
if self.debug:
|
106
|
+
print(f"{sql}\n{hits}")
|
107
|
+
|
108
|
+
if len(hits) == 0:
|
109
|
+
# Functional dependency found: it's not the case that there's more than one value (field_2)
|
110
|
+
# associated with field_1
|
111
|
+
fds.add_dependency(attr1_var, attr2_var)
|
112
|
+
self.fds = fds
|
113
|
+
return fds
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
5
5
|
# SPDX-FileCopyrightText: 2000-2022 Jens Lechtenbörger
|
6
6
|
|
7
|
-
|
7
|
+
"""Functional dependencies (FDs) are a major tool for database design.
|
8
8
|
|
9
9
|
Introduction
|
10
10
|
============
|
@@ -416,8 +416,9 @@ class FD(object):
|
|
416
416
|
singleton.add(attr)
|
417
417
|
if attr in self.lhs:
|
418
418
|
# FDs of the form XA->A are trivial.
|
419
|
-
logging.debug(
|
420
|
-
|
419
|
+
logging.debug(
|
420
|
+
"rminimize: omitted trivial FD %s", FD(self.lhs, singleton)
|
421
|
+
)
|
421
422
|
continue
|
422
423
|
result.add(FD(self.lhs.copy(), singleton))
|
423
424
|
return result
|
@@ -490,7 +491,10 @@ class FDSet(set):
|
|
490
491
|
else:
|
491
492
|
logging.debug(
|
492
493
|
"lminimize: removed %s for rhs %s: new lhs: %s",
|
493
|
-
attr,
|
494
|
+
attr,
|
495
|
+
_set2string(rhs),
|
496
|
+
_set2string(cand),
|
497
|
+
)
|
494
498
|
return cand
|
495
499
|
|
496
500
|
def key(self, attributes=None):
|
@@ -527,8 +531,8 @@ class FDSet(set):
|
|
527
531
|
if lhs != fdep.lhs:
|
528
532
|
result.remove(fdep)
|
529
533
|
logging.debug(
|
530
|
-
"basis: replaced lhs in %s with %s",
|
531
|
-
|
534
|
+
"basis: replaced lhs in %s with %s", fdep, _set2string(lhs)
|
535
|
+
)
|
532
536
|
result.add(FD(lhs, fdep.rhs))
|
533
537
|
|
534
538
|
# Third, remove redundant FDs. (An FD is redundant if it is implied
|
@@ -559,8 +563,7 @@ class RelSchema(object):
|
|
559
563
|
|
560
564
|
def __str__(self):
|
561
565
|
"""Return relation schema in usual pair representation."""
|
562
|
-
return "({}, {})".format(_set2string(self.attributes),
|
563
|
-
_set2string(self.fds))
|
566
|
+
return "({}, {})".format(_set2string(self.attributes), _set2string(self.fds))
|
564
567
|
|
565
568
|
def key(self):
|
566
569
|
"""Return a key."""
|
@@ -631,6 +634,7 @@ def extract_by_lhs(basis, fdep):
|
|
631
634
|
|
632
635
|
|
633
636
|
# Run doctests by default.
|
634
|
-
if __name__ ==
|
637
|
+
if __name__ == "__main__":
|
635
638
|
import doctest
|
636
|
-
|
639
|
+
|
640
|
+
print(doctest.testmod())
|
@@ -0,0 +1,207 @@
|
|
1
|
+
"""
|
2
|
+
Created on 2022-06-08
|
3
|
+
|
4
|
+
@author: wf
|
5
|
+
"""
|
6
|
+
from enum import Enum
|
7
|
+
import copy
|
8
|
+
|
9
|
+
|
10
|
+
class Notation(str, Enum):
|
11
|
+
"""
|
12
|
+
a notation to be used for stringifcation
|
13
|
+
"""
|
14
|
+
|
15
|
+
math = "LaTex math symbols"
|
16
|
+
utf8 = "UTF-8 Symbols"
|
17
|
+
plain = "plaintext ASCII"
|
18
|
+
short = "short notation without delimiters using juxtapositions"
|
19
|
+
|
20
|
+
|
21
|
+
class Set(set):
|
22
|
+
"""
|
23
|
+
https://docs.python.org/3/tutorial/datastructures.html#sets
|
24
|
+
"""
|
25
|
+
|
26
|
+
notation = Notation.plain
|
27
|
+
|
28
|
+
def __init__(self, *args, **kwargs):
|
29
|
+
"""
|
30
|
+
constructor
|
31
|
+
"""
|
32
|
+
super().__init__(*args, **kwargs)
|
33
|
+
|
34
|
+
def __str__(self):
|
35
|
+
text = Set.stringify_set(self, notation=Set.notation)
|
36
|
+
return text
|
37
|
+
|
38
|
+
@classmethod
|
39
|
+
def stringify_set(cls, pSet, notation: Notation):
|
40
|
+
"""
|
41
|
+
return a string representation of the given set using the given delimiter
|
42
|
+
|
43
|
+
Args:
|
44
|
+
pSet(set): the set to stringify
|
45
|
+
delim(str): the delimiter to use
|
46
|
+
"""
|
47
|
+
elementTexts = []
|
48
|
+
for elem in pSet:
|
49
|
+
elementTexts.append(str(elem))
|
50
|
+
sortedElements = sorted(elementTexts)
|
51
|
+
if len(sortedElements) == 0:
|
52
|
+
# empty set
|
53
|
+
if notation == Notation.math:
|
54
|
+
text = "\emptyset"
|
55
|
+
else:
|
56
|
+
text = "∅"
|
57
|
+
else:
|
58
|
+
elementDelim = ","
|
59
|
+
if notation == Notation.short:
|
60
|
+
elementDelim = "" # Thin space would be better
|
61
|
+
if notation == Notation.short:
|
62
|
+
text = ""
|
63
|
+
elif notation == Notation.math:
|
64
|
+
text = "\{"
|
65
|
+
else:
|
66
|
+
text = "{"
|
67
|
+
|
68
|
+
delim = ""
|
69
|
+
for element in sortedElements:
|
70
|
+
text += f"{delim}{element}"
|
71
|
+
delim = elementDelim
|
72
|
+
if notation == Notation.short:
|
73
|
+
pass
|
74
|
+
elif notation == Notation.math:
|
75
|
+
text += "\}"
|
76
|
+
else:
|
77
|
+
text += "}"
|
78
|
+
return text
|
79
|
+
|
80
|
+
|
81
|
+
class FD(object):
|
82
|
+
"""A functional dependency with left- and right-hand side."""
|
83
|
+
|
84
|
+
notation = Notation.plain
|
85
|
+
|
86
|
+
def __init__(self, left, right):
|
87
|
+
"""Create FD with left hand side and right hand side
|
88
|
+
|
89
|
+
Args:
|
90
|
+
left(object): set of attributes for the left hand side
|
91
|
+
right(object): set of attributes for the right hand side
|
92
|
+
|
93
|
+
"""
|
94
|
+
self.left = Set(left)
|
95
|
+
self.right = Set(right)
|
96
|
+
|
97
|
+
def __str__(self):
|
98
|
+
"""
|
99
|
+
convert me to a string
|
100
|
+
|
101
|
+
Return:
|
102
|
+
str: a string representation of myself
|
103
|
+
"""
|
104
|
+
text = FD.stringify_FD(self, FD.notation)
|
105
|
+
return text
|
106
|
+
|
107
|
+
@classmethod
|
108
|
+
def stringify_FD(cls, fd, notation: Notation):
|
109
|
+
"""
|
110
|
+
Return:
|
111
|
+
a string representation of the given Functional Dependency
|
112
|
+
"""
|
113
|
+
setNotation = Notation.short
|
114
|
+
leftText = Set.stringify_set(fd.left, notation=setNotation)
|
115
|
+
rightText = Set.stringify_set(fd.right, notation=setNotation)
|
116
|
+
if notation == Notation.utf8:
|
117
|
+
to = "→"
|
118
|
+
elif notation == Notation.math:
|
119
|
+
to = " \to "
|
120
|
+
else:
|
121
|
+
to = "->"
|
122
|
+
text = f"{leftText}{to}{rightText}"
|
123
|
+
return text
|
124
|
+
|
125
|
+
|
126
|
+
class Attribute:
|
127
|
+
"""
|
128
|
+
an Attribute e.g.
|
129
|
+
Example: Attribute('A', 'Wikidata identifier', 'Wikidata-Schlüssel')
|
130
|
+
"""
|
131
|
+
|
132
|
+
def __init__(self, var_name: str, english_name: str, german_name: str):
|
133
|
+
"""
|
134
|
+
constructor
|
135
|
+
|
136
|
+
Args:
|
137
|
+
var_name(str): the Variable name
|
138
|
+
english_name(str): the english name
|
139
|
+
german_name(str): the german name
|
140
|
+
|
141
|
+
"""
|
142
|
+
self.var_name = var_name
|
143
|
+
self.german_name = german_name
|
144
|
+
self.english_name = english_name
|
145
|
+
|
146
|
+
def __str__(self):
|
147
|
+
text = f"{self.var_name}≡{self.english_name}≡{self.german_name}"
|
148
|
+
return text
|
149
|
+
|
150
|
+
|
151
|
+
class RelSchema(object):
|
152
|
+
"""A relation schema consists of a set of attributes and a set of FDs.
|
153
|
+
|
154
|
+
Various normal forms exist to describe "good" schemata. Normalization
|
155
|
+
is the process of creating schemata that satisfy certain normal forms.
|
156
|
+
The class of synthesis algorithms targets 3NF.
|
157
|
+
"""
|
158
|
+
|
159
|
+
def __init__(
|
160
|
+
self, attributes: Set, fds, name: str = "R", notation: Notation = None
|
161
|
+
):
|
162
|
+
"""
|
163
|
+
Construct relation schema with attributes and FDs.
|
164
|
+
|
165
|
+
Args:
|
166
|
+
attributes(list): a list of Attributes
|
167
|
+
fds(FunctionalDependencySet): a set of FDs
|
168
|
+
name(string): the name of the RelatonSchema
|
169
|
+
notation(Notation): the notation to use
|
170
|
+
"""
|
171
|
+
if notation is None:
|
172
|
+
notation = Notation.utf8
|
173
|
+
self.notation = notation
|
174
|
+
self.attributes = attributes
|
175
|
+
self.fds = fds
|
176
|
+
self.name = name
|
177
|
+
|
178
|
+
def __str__(self):
|
179
|
+
text = RelSchema.stringify(self, self.notation)
|
180
|
+
return text
|
181
|
+
|
182
|
+
@classmethod
|
183
|
+
def stringify(cls, rs, notation):
|
184
|
+
"""
|
185
|
+
return a textual representation of the given relational schema in the given notation
|
186
|
+
"""
|
187
|
+
attrText = Set.stringify_set(rs.attributes, notation)
|
188
|
+
fds = copy.deepcopy(rs.fds)
|
189
|
+
fds.notation = notation
|
190
|
+
fdsText = str(fds)
|
191
|
+
rsSet = Set([attrText, fdsText])
|
192
|
+
rsSetText = Set.stringify_set(rsSet, notation=notation)
|
193
|
+
rsText = f"{rs.name}={rsSetText}"
|
194
|
+
return rsText
|
195
|
+
|
196
|
+
def findCandidateKeys(self):
|
197
|
+
"""
|
198
|
+
find candidate keys
|
199
|
+
"""
|
200
|
+
cklist = []
|
201
|
+
for foundKeySet in self.fds.find_candidate_keys():
|
202
|
+
keySet = Set()
|
203
|
+
for foundKey in foundKeySet:
|
204
|
+
attr = self.fds.attribute_map[foundKey]
|
205
|
+
keySet.update([attr])
|
206
|
+
cklist.append(keySet)
|
207
|
+
return cklist
|
@@ -0,0 +1,178 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: dbis-functional-dependencies
|
3
|
+
Version: 1.0.0
|
4
|
+
Summary: RWTH Aachen Computer Science i5/dbis assets for Lecture Datenbanken und Informationssysteme
|
5
|
+
Author-email: DBIS i5 RWTH Aachen <dbis-vl@dbis.rwth-aachen.de>
|
6
|
+
Project-URL: Homepage, https://git.rwth-aachen.de/i5/teaching/dbis/dbis-functional-dependencies
|
7
|
+
Classifier: Programming Language :: Python :: 3.10
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
9
|
+
Requires-Python: >=3.10
|
10
|
+
Description-Content-Type: text/markdown
|
11
|
+
License-File: LICENSE
|
12
|
+
Requires-Dist: graphviz ~=0.20
|
13
|
+
Requires-Dist: networkx ~=2.7
|
14
|
+
Requires-Dist: numpy ~=1.26
|
15
|
+
Requires-Dist: sphinx ~=7.2
|
16
|
+
Requires-Dist: sphinxcontrib-apidoc ~=0.4
|
17
|
+
Requires-Dist: functional-dependencies ~=1.3
|
18
|
+
Requires-Dist: build ~=1.0
|
19
|
+
Requires-Dist: pylodstorage ~=0.4.11
|
20
|
+
Provides-Extra: build
|
21
|
+
Requires-Dist: twine ==4.* ; extra == 'build'
|
22
|
+
Requires-Dist: build ==1.* ; extra == 'build'
|
23
|
+
Provides-Extra: test
|
24
|
+
Requires-Dist: black ==23.12.1 ; extra == 'test'
|
25
|
+
|
26
|
+
# DBIS Functional Dependencies
|
27
|
+
|
28
|
+
Functional Dependencies and Normal Forms for Relational Databases
|
29
|
+
|
30
|
+
[](https://pypi.python.org/pypi/dbis-functional-dependencies/)
|
31
|
+
[](https://pypi.org/project/dbis-functional-dependencies/)
|
32
|
+
[](https://www.apache.org/licenses/LICENSE-2.0)
|
33
|
+
[](https://git.rwth-aachen.de/i5/teaching/dbis/dbis-functional-dependencies/-/packages)
|
34
|
+
|
35
|
+
This library provides a Python implementation of the [synthesis algorithm](https://de.wikipedia.org/wiki/Synthesealgorithmus) and decomposition algorithm according to the DBIS lecture. For more background and application of functional dependencies and the algorithms, see [Doku-FunctionalDependencies](https://git.rwth-aachen.de/i5/teaching/dbis-jupyter/dbis-ss-2023-test/-/blob/main/doku/Doku-FunctionalDependencies.ipynb).
|
36
|
+
|
37
|
+
# Features
|
38
|
+
- Create sets of Functional dependencies (FDSets).
|
39
|
+
- Calculate candidate keys of an FDSet.
|
40
|
+
- Calculate attribute closure of an attribute or set of attributes.
|
41
|
+
- Test whether an FDSet is in 2NF, 3NF or BCNF.
|
42
|
+
- Execute the synthesis algorithm to transform the FDSet into 3NF.
|
43
|
+
- Execute the decomposition algorithm to transform the FDSet into BCNF.
|
44
|
+
- Generate the closure $F^+$ of an FDSet $F$.
|
45
|
+
- Generate true/false questions w.r.t. synthesis and decomposition algorithm.
|
46
|
+
|
47
|
+
# Installation
|
48
|
+
Install via pip:
|
49
|
+
```bash
|
50
|
+
pip install dbis-functional-dependencies
|
51
|
+
```
|
52
|
+
|
53
|
+
# Usage
|
54
|
+
### Creating an FDSet
|
55
|
+
Create a new instance of `FunctionalDependencySet`. The set of attributes is passed as parameter.
|
56
|
+
```python
|
57
|
+
fdset = FunctionalDependencySet('ABCDE')
|
58
|
+
```
|
59
|
+
You can add more attributes later by using the `add_attribute` function.
|
60
|
+
```python
|
61
|
+
fdset.add_attribute('F')
|
62
|
+
```
|
63
|
+
Add dependencies with the `add_dependency` function ...
|
64
|
+
```python
|
65
|
+
fdset.add_dependency("AC", "DE")
|
66
|
+
fdset.add_dependency("DEF", "B")
|
67
|
+
fdset.add_dependency("B", "D")
|
68
|
+
```
|
69
|
+
... or remove them with the `remove_dependency` function.
|
70
|
+
```python
|
71
|
+
fdset.remove_dependency("B", "D")
|
72
|
+
```
|
73
|
+
|
74
|
+
Printing an FDSet shows the dependencies in a more readable form.
|
75
|
+
```python
|
76
|
+
print(f"{fdset}")
|
77
|
+
```
|
78
|
+
|
79
|
+
### Attribute closure and candidate keys
|
80
|
+
Calculate the attribute closure of one or multiple attributes.
|
81
|
+
```python
|
82
|
+
closureA = fdset.get_attr_closure('A')
|
83
|
+
closureAC = fdset.get_attr_closure('AC')
|
84
|
+
```
|
85
|
+
|
86
|
+
Calculate all candidate keys.
|
87
|
+
```python
|
88
|
+
ckeys = fdset.find_candidate_keys()
|
89
|
+
```
|
90
|
+
|
91
|
+
### Check for normal forms
|
92
|
+
Since we only work with schemas (no actual values for the attributes), we assume that a corresponding database is in 1NF.
|
93
|
+
|
94
|
+
Check whether the FDSet is in 2NF, 3NF or BCNF.
|
95
|
+
```python
|
96
|
+
is2NF = fdset.is2NF()
|
97
|
+
is3NF = fdset.is3NF()
|
98
|
+
isBCNF = fdset.isBCNF()
|
99
|
+
```
|
100
|
+
|
101
|
+
### Execute the synthesis algorithm
|
102
|
+
Execute the synthesis algorithm on an FDSet to generate a corresponding list of FDSets in 3NF.
|
103
|
+
```python
|
104
|
+
fdslist = fdset.synthesize()
|
105
|
+
```
|
106
|
+
The algorithm performs the following steps:
|
107
|
+
|
108
|
+
0. Find the candidate keys.
|
109
|
+
1. Calculate the canonical cover.
|
110
|
+
- left reduction
|
111
|
+
- right reduction
|
112
|
+
- remove dependencies with empty rhs
|
113
|
+
- combine dependencies with same lhs
|
114
|
+
2. Create a new relation for every dependency in the canonical cover.
|
115
|
+
3. Create the optional key scheme if no candidate key is included in the attribute set of one of the relations of step 2.
|
116
|
+
4. Remove subset relations.
|
117
|
+
|
118
|
+
You receive additional information on the steps of the algorithm by toggling the parameter `verbose`.
|
119
|
+
```python
|
120
|
+
fdslist = fdset.synthesize(vebose=True)
|
121
|
+
```
|
122
|
+
Alternatively, you can also execute the single steps with the following functions:
|
123
|
+
```python
|
124
|
+
fdset_step.canonical_cover()
|
125
|
+
fdslist_step = fdset_step.create_new_fdsets()
|
126
|
+
fdslist_step_with_key = FunctionalDependencySet.create_optional_key_scheme(self, ckeys, fdslist_step)
|
127
|
+
reduced_fdslist_step = FunctionalDependencySet.remove_subset_relations(self, fdslist_step_with_key)
|
128
|
+
```
|
129
|
+
The verbose option exists for all steps.
|
130
|
+
|
131
|
+
### Execute the decomposition algorithm
|
132
|
+
Execute the decomposition algorithm on an FDSet to generate a corresponding decomposition of FDSets in BCNF.
|
133
|
+
```python
|
134
|
+
fdslist = fdset.decompose2()
|
135
|
+
```
|
136
|
+
Before performing the actual algorithm, the the closure of the FDSet is calculated.
|
137
|
+
|
138
|
+
### Closure of an FDSet
|
139
|
+
Calculate the closure $F^+$ of an FDSet $F$.
|
140
|
+
```python
|
141
|
+
fdset.completeFDsetToClosure()
|
142
|
+
```
|
143
|
+
This function just adds dependencies with all subset combinations of the attribute set with their corresponding closures on the rhs of the dependency, so that no implicit dependency is missed by the decomposition algorithm.
|
144
|
+
|
145
|
+
### Exercise generator
|
146
|
+
Generate true/false statements based on the different steps of the algorithms.
|
147
|
+
```python
|
148
|
+
fdslist = fdset.synthesize(genEx=True)
|
149
|
+
```
|
150
|
+
The `genEx` option is available for the following functions:
|
151
|
+
* `find_candidate_keys`
|
152
|
+
* `synthesize`
|
153
|
+
* `canonical_cover`
|
154
|
+
* `left_reduction`
|
155
|
+
* `right_reduction`
|
156
|
+
* `remove_empty_fds`
|
157
|
+
* `combine_fds`
|
158
|
+
* `create_new_fdsets`
|
159
|
+
* `create_optional_key_scheme`
|
160
|
+
* `remove_subset_relations`
|
161
|
+
* `decompose2`
|
162
|
+
|
163
|
+
### Checking results against expected
|
164
|
+
Checks a given calculated step of an FDSet during the synthesis algorithm (according to the DBIS lecture) for correctness.
|
165
|
+
```python
|
166
|
+
original = fdset.copy()
|
167
|
+
fdset.left_reduction()
|
168
|
+
original.isCorrectLeftReduction(fdset)
|
169
|
+
```
|
170
|
+
For this purpose, the following functions exist:
|
171
|
+
* `isCorrectLeftReduction`
|
172
|
+
* `isCorrectRightReduction`
|
173
|
+
* `isCorrectRemovingEmptyFDs`
|
174
|
+
* `isCorrectCombinationOfDependencies`
|
175
|
+
* `isCorrectCanonicalCover`
|
176
|
+
* `isCorrectCreationOfNewFDS`
|
177
|
+
|
178
|
+
These functions are called on the FDSet with all steps before already calculated on it.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
dbis_functional_dependencies/BCNF.py,sha256=7EosTQTPl07W_UIiTp41PoxM9OTWMIzwA5cdQxbrkyM,68688
|
2
|
+
dbis_functional_dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
dbis_functional_dependencies/fdcheck.py,sha256=wNFJb2WZGkvQxN8Prbcd6DhuXYnQ4Zm9HsoLxc7RU7k,4082
|
4
|
+
dbis_functional_dependencies/fds.py,sha256=CLssS0L-3NxodSCtEcOMG5lxy0elLEaWkK8cT9IUszo,24021
|
5
|
+
dbis_functional_dependencies/fdsbase.py,sha256=wgt1tjM-EyD09v8Yf57UgoZB6ptvijIuE0WfgdKj43M,5767
|
6
|
+
dbis_functional_dependencies-1.0.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
7
|
+
dbis_functional_dependencies-1.0.0.dist-info/METADATA,sha256=zvpMH7aEjXT0x4umngOYQKEqpln54NgK4YXqCOTiH9w,6774
|
8
|
+
dbis_functional_dependencies-1.0.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
9
|
+
dbis_functional_dependencies-1.0.0.dist-info/top_level.txt,sha256=Kxq61kOpu1YSAAigxyqE3_XCyftviQ1TW55Q1oxEP-4,29
|
10
|
+
dbis_functional_dependencies-1.0.0.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
dbis_functional_dependencies
|
@@ -1,23 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: dbis-functional-dependencies
|
3
|
-
Version: 0.0.7
|
4
|
-
Summary: RWTH Aachen Computer Science i5/dbis assets for Lecture Datenbanken und Informationssysteme
|
5
|
-
Home-page: https://git.rwth-aachen.de/i5/teaching/dbis-functional-dependencies
|
6
|
-
Author: Wolfgang Fahl
|
7
|
-
Author-email: fahl@dbis.rwth-aachen.de
|
8
|
-
License: Apache
|
9
|
-
Classifier: Programming Language :: Python
|
10
|
-
Classifier: Programming Language :: Python :: 3.7
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
14
|
-
Description-Content-Type: text/markdown
|
15
|
-
License-File: LICENSE
|
16
|
-
|
17
|
-
# dbis-functional-dependencies
|
18
|
-
|
19
|
-
Functional Dependencies and Normal Forms for Relational Databases
|
20
|
-
|
21
|
-
[](https://pypi.python.org/pypi/dbis-functional-dependencies/)
|
22
|
-
[](https://pypi.org/project/dbis-functional-dependencies/)
|
23
|
-
[](https://www.apache.org/licenses/LICENSE-2.0)
|
@@ -1,10 +0,0 @@
|
|
1
|
-
functional_dependencies/BCNF.py,sha256=O9mzLeUfOOD9891-DOl7soJmzEGpPEmyXRuJnfOLDhE,22485
|
2
|
-
functional_dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
functional_dependencies/fdcheck.py,sha256=z1gk1DY0O6bATFCD0mQsrVRJ9FmcusQ0TmIPu97JMUk,3854
|
4
|
-
functional_dependencies/fds.py,sha256=jYVc4Dlo8wZutIq7CUo5znIYoDuooCxu3U6qhVWRGhc,23990
|
5
|
-
functional_dependencies/fdsbase.py,sha256=Lx2KO2W9LngGHJHK6bhapWJr7P72xaJ_SUDVyyrCaz8,5683
|
6
|
-
dbis_functional_dependencies-0.0.7.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
7
|
-
dbis_functional_dependencies-0.0.7.dist-info/METADATA,sha256=2U_HSE5_NVYgWAYbdgilZerfVil8tu6JG45BgvudbwE,1129
|
8
|
-
dbis_functional_dependencies-0.0.7.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
|
9
|
-
dbis_functional_dependencies-0.0.7.dist-info/top_level.txt,sha256=gdKnqm_LuTOPR2jukC8CfBSBQTbgvYbKJOSjJtM1woo,24
|
10
|
-
dbis_functional_dependencies-0.0.7.dist-info/RECORD,,
|
@@ -1 +0,0 @@
|
|
1
|
-
functional_dependencies
|