As described here, decomposition of R into R1 and R2 is lossless if
- Attributes(R1) U Attributes(R2) = Attributes(R)
- Attributes(R1) ∩ Attributes(R2) ≠ Φ
- Common attribute must be a key for at least one relation (R1 or R2)
EDIT
with the assumption that only the non-trivial cases are considered here, I think OP intended that too (so that (2) holds under this non-trivial assumption):
e.g., not considering the trivial corner case where all tuples of R1 / R2 are unique, i.e., the empty set {} is a key (as @philipxy pointed out), hence any decomposition will be lossless and hence not interesting (since spurious tuples cant be created upon joining) - the corner cases for which decomposition can be lossless despite
Attributes(R1) ∩ Attributes(R2) = Φ are ruled out.
We can check the above conditions with the following python
code snippet:
def closure(s, fds):
c = s
for f in fds:
l, r = f[0], f[1]
if l.issubset(c):
c = c.union(r)
if s != c:
c = closure(c, fds)
return c
def is_supekey(s, rel, fds):
c = closure(s, fds)
print(f'({"".join(sorted(s))})+ = {"".join(sorted(c))}')
return rel.issubset(c) #c == rel
def is_lossless_decomp(r1, r2, r, fds):
c = r1.intersection(r2)
if r1.union(r2) != r:
print('not lossless: R1 U R2 ≠ R!')
return False
if r1.union(r2) != r or len(c) == 0:
print('not lossless: no common attribute in between R1 and R2!')
return False
if not is_supekey(c, r1, fds) and not is_supekey(c, r2, fds):
print(f'not lossless: common attribute {"".join(c)} not a key in R1 or R2!')
return False
print('lossless decomposition!')
return True
To process the given FDs in standard form, to convert to suitable data structure, we can use the following function:
import re
def process_fds(fds):
pfds = []
for fd in fds:
fd = re.sub('\s+', '', fd)
l, r = fd.split('->')
pfds.append([set(list(l)), set(list(r))])
return pfds
Now let's test with the above decompositions given:
R = {'A','B','C','D','E'}
fds = process_fds(['AB->E', 'C->AD', 'D->B', 'E->C'])
R1, R2 = {'A', 'C', 'D'}, {'B', 'C', 'E'}
is_lossless_decomp(R1, R2, R, fds)
# (C)+ = ACD
# lossless decomposition!
R1, R2 = {'A', 'C', 'D'}, {'A', 'B', 'E'}
is_lossless_decomp(R1, R2, R, fds)
# (A)+ = A
# not lossless: common attribute A not a key in R1 or R2!