关系数据库设计 - 两个关系1:1或1:1?

时间:2010-10-29 19:42:40

标签: python database-design sqlalchemy relational-database modeling

这个问题是关于如何设计SQL关系的。我在这个问题上相当新手,我想知道(方式)更多专家的答案......

我目前正在使用MeGrok和SqlAlchemy将ZopeDB(面向对象)数据库迁移到MySQL(关系)(尽管我认为这不太相关,因为我的问题更多是关于在关系数据库中设计关系)。

我有两个相关的课程:

class Child(object):
    def __init__(self):
        self.field1 = "hello world"

class Parent(object):
    def __init__(self):
        self.child1 = Child()
        self.child2 = Child()

“Parent”类有两个不同的Child()类实例。我甚至不确定如何对待这个(两个不同的1:1关系或1:2的关系)。

目前,我有这个:

class Child(rdb.Model):
    rdb.metadata(metadata)
    rdb.tablename("children_table")
    id = Column("id", Integer, primary_key=True)
    field1 = Column("field1", String(64))   #Irrelevant
    def __init__(self):
        self.field1 = "hello world"

class Parent(rdb.Model):
    rdb.metadata(metadata)
    rdb.tablename("parent_table")

    id = Column("id", Integer, primary_key=True)
    child1_id = Column("child_1_id", Integer, ForeignKey("children_table.id"))
    child2_id = Column("child_2_id", Integer, ForeignKey("children_table.id"))

    child1 = relationship(Child,
        primaryjoin = ("parent_table.child1_id == children_table.id")
    )

    child2 = relationship(Child,
        primaryjoin = ("parent_table.child2_id == children_table.id")
    ) 

含义......好的,我将两个“子”id作为外键存储在Parent中,并使用该信息检索子项本身。

这工作正常,但我不知道这是否是最合适的解决方案。

或者我可以这样做:

class Child(rdb.Model):
    rdb.metadata(metadata)
    rdb.tablename("children_table")
    id = Column("id", Integer, primary_key=True)
    parent_id = Column("id", Integer, ForeignKey("parent_table.id"))  # New!
    type = Column("type", ShortInteger) # New!

    field1 = Column("field1", String(64))  #Irrelevant
    def __init__(self):
        self.field1 = "hello world"

class Parent(rdb.Model):
    rdb.metadata(metadata)
    rdb.tablename("parent_table")

    id = Column("id", Integer, primary_key=True)
    child1 = relationship(
        # Well... this I still don't know how to write it down,
        # but it would be something like:
        #   Give me all the children whose "parent_id" is my own "id"
        #   AND their type == 1
        # I'll deal with the joins and the actual implementation depending 
        # on your answer, guys
    )

    child2 = relationship(
        #  Would be same as above
        #  but selecting children whose type == 2
    )

这可能适合将新子项添加到父类...如果我添加“Parent.child3”,我只需要创建一个与现有关系非常相似的新关系。

我现在拥有它的方式意味着创建一个新的关系并向父母添加一个新的外键。

另外,拥有带有一堆外键的“父”表可能不会使它成为世界上最好的“父”表,对吗?

我想知道对数据库有更多了解的人们认为:)

谢谢。

PS:相关帖子? Question 3998545

3 个答案:

答案 0 :(得分:3)

在回复评论时扩大

问题是,你正在思考你所知道的术语(可理解的),并且你有OO数据库的局限性......这对于继续进入Relational数据库是不利的。因此,出于多种原因,最好只是简单地识别实体和关系,并将它们标准化。您用来调用的方法很容易更改,您不仅限于现在拥有的方法。

这里有一些好的答案,但即使是那些也是有限且不完整的。如果你规范父母和孩子(作为人,他们将有许多公共列),你得到Person,没有重复的列。

人们与其他人,他们的父母有“向上”的关系,但那是背景,而不是父母首先作为一个人存在的事实(如果你愿意,你可以拥有两个以上)。人们也与子女有“向下”的关系,也有语境。每个父母两个孩子的限制是荒谬的(你可能需要检查你的方法/类:我怀疑一个是“向上”导航而另一个是“向下”)。并且您不希望将关系存储为重复(一旦Fred是Sally的父亲; Sally是Fred的孩子的两倍),单个事实存在于单行中,可以解释为Parent⇢Child或Parent⇠Child。

这个要求出现在许多问题中,因此我使用的是一个通用但详细的插图。该模型定义了需要向上或向下移动的任何树结构,通过简单递归处理。它被称为物料清单结构,最初是为库存控制系统创建的,可以应用于任何树结构要求。这是第五范式;没有重复的列;没有更新异常。

Bill of Materials

对于具有许多常用列的程序集和组件,它们被标准化为零件;它们是程序集还是组件是上下文的,这些上下文列位于关联(多对多)表中。

两个关系1:1还是1:2?

实际上,它是两次1 :: n。

普通或排名在主键中是明确的(按时间顺序排列)。如果需要其他一些序数,只需在Associative表中添加一列即可。更好的是,它确实是一个派生列,因此在运行时从当前值计算它。

答案 1 :(得分:1)

这可能有点脱离背景,因为我没有使用你提到的任何东西 - 但就一般设计而言,这里有几个想法:

  1. 根据常见类型保持关系:has_one,has_many,belongs_to,has_and_belongs_to_many。
  2. 对于孩子,最好不要明确指定N个孩子;或者没有,一个,或者可能有很多。因此,child1child2的模型声明将被单个属性替换 - 包含子项的数组。
  3. 说实话,我不知道你的使用情况有多好。然而,这通常是关系在ORM意义上的运作方式。所以,基于此:

    1. 如果模型属于另一个模型(它具有另一个表的外键),它将具有parent [sic]属性,并引用父对象
    2. 如果模型有一个属于它的模型(另一个模型具有第一个模型表的外键),它将具有child [sic]属性,并引用子对象
    3. 如果模型有许多属于它的模型(许多其他模型具有第一个模型表的外键),它将具有children [sic]属性,该属性是对子对象的引用数组< / LI>
    4. 如果模型具有并且属于许多其他模型......您可能需要考虑使用parentschildren属性,或类似的东西;命名法不如你访问它所属的一组模型,以及属于它的另一组模型重要。
    5. 很抱歉,如果这完全没有用,但它可能会有所帮助。 HTH。

答案 2 :(得分:0)

我承认我不太熟悉对象数据库,但从关系角度来看,这是一个简单的一对多(可选)关系。

create table parent (
  id           int PK,
  otherField   whatever
)

create table child (
  id           int PK,
  parent_id    int Fk,
  otherField   whatever
)

显然,这不是可用的代码......

认为这与你的第二个例子类似。如果您需要跟踪孩子与父母的关系中的序数位置,您可以在子表中添加一列,例如:

create table child (
  id           int PK,
  parent_id    int Fk,
  birth_order  int,
  otherField   whatever
)

您必须负责在应用程序级别管理该字段,这不是您期望DBMS为您做的事情。

我认为这是一种可选择的关系,假设无子女的父母可以存在 - 如果不是这样,那么它就成了一个必要的关系逻辑上,尽管你仍然需要让DBMS创建一个新的父记录无子地,然后抓住它的id来创建孩子 - 并再次在应用程序级别管理需求。