키 속성 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
"""키 속성 검증.
문서: 21.키 속성 (예시로 보기, 카드 C1~C3)
출처: Elmasri & Navathe, Fundamentals of Database Systems 3장의 CAR 예와 COMPANY의 DEPARTMENT
주장:
1. 문서의 CAR 개체 집합에서 번호판 Number만으로는 차가 가려지지 않는다('ABC 123'이 두 대).
State만으로도 안 된다(TEXAS가 두 대). 둘을 묶은 Registration(Number, State)은 세 대가 다 다르다.
2. Vehicle_id도 세 대가 다 다르다. 그래서 CAR에는 키가 둘 있다(Registration, Vehicle_id).
3. Year도 세 대가 우연히 다 다르지만(2004, 2005, 2002) 키가 아니다.
같은 해에 만든 차가 한 대 들어오면 깨진다. 지금 데이터에서 안 겹치는 것만으로는 키라고 못 한다.
4. "키가 둘"과 "둘을 묶은 키 하나"는 다르다: DEPARTMENT의 Name, Number는 각각 혼자서 키이고,
CAR의 Number, State는 묶어야만 키다.
"""
from itertools import combinations
ATTRS = ("Number", "State", "Vehicle_id", "Make", "Model", "Year")
# 문서의 CAR 개체 집합. Color(다치 속성)는 키 후보가 아니라 뺐다.
CARS = [
("ABC 123", "TEXAS", "TK629", "Ford Mustang", "convertible", 2004),
("ABC 123", "NEW YORK", "WP9872", "Nissan Maxima", "4-door", 2005),
("VSY 720", "TEXAS", "TD729", "Chrysler LeBaron", "4-door", 2002),
]
def unique(rows, names, attrs=ATTRS):
"""rows에서 names 속성들의 값 묶음이 개체마다 다른가."""
idx = [attrs.index(n) for n in names]
seen = [tuple(r[i] for i in idx) for r in rows]
return len(set(seen)) == len(seen)
def main():
assert not unique(CARS, ["Number"]) and not unique(CARS, ["State"])
assert unique(CARS, ["Number", "State"])
print("[OK] Number만, State만으로는 겹친다. (Number, State)는 세 대가 다 다르다")
assert unique(CARS, ["Vehicle_id"])
print("[OK] Vehicle_id는 세 대가 다 다르다 -> CAR의 키는 Registration과 Vehicle_id 둘")
# 지금 데이터에서 혼자 안 겹치는 속성을 모두 센다
singles = [a for a in ATTRS if unique(CARS, [a])]
assert singles == ["Vehicle_id", "Make", "Year"]
print("[OK] 이 세 대에서 혼자 안 겹치는 속성:", singles)
# Year는 우연히 안 겹칠 뿐이다: 2004년식 차를 한 대 더 넣으면 깨진다
more = CARS + [("KLM 555", "OHIO", "ZZ101", "Honda Civic", "4-door", 2004)]
assert not unique(more, ["Year"])
assert unique(more, ["Vehicle_id"]) and unique(more, ["Number", "State"])
print("[OK] 2004년식 한 대를 넣으면 Year는 겹치고, 두 키는 그대로 안 겹친다")
# 묶어야만 키인 것과 각각 키인 것
pairs = [c for c in combinations(["Number", "State"], 1) if unique(CARS, list(c))]
assert pairs == []
departments = [("Research", 5), ("Administration", 4), ("Headquarters", 1)] # COMPANY 상태의 DEPARTMENT
assert unique(departments, ["Name"], ("Name", "Number")) and unique(departments, ["Number"], ("Name", "Number"))
print("[OK] CAR의 Number, State는 묶어야만 다르고, DEPARTMENT의 Name, Number는 각각 다르다")
print("ALL CHECKS PASSED")
if __name__ == "__main__":
main()