【问题标题】:join between two many-to-many tables连接两个多对多表
【发布时间】:2014-12-04 08:11:28
【问题描述】:

我在为基于 Java 的 Web 应用程序定义适当的数据库设计时遇到了一些麻烦。我有 3 个表(标签、DT 和属性),我的场景如下。每个 Tag 可以包含多个 Dts,每个 DT 可以分配给不同的 Tag,每个 DT 有多个 Properties,每个 Property 可以在多个 DT 中使用。将 DT 分配给 Tag 时,用户可以为其所有属性设置值。

我的问题是,我如何定义一个关系,以便每个标签都可以根据分配给它的 DT 具有其属性值。我的设计中缺少什么表格和关系。然后我必须基于这个数据库创建相对实体。

Tag1==> DT1 ==> Initial values1, property values set 1 ; 
Tag2==> DT1 ==> Initial values1, property values set 2 ;

这是数据库图。并提前感谢您的帮助。

这是一些示例数据

【问题讨论】:

  • 如果您能提供您心目中的示例数据,那就太好了。没有它,设计看起来过于复杂。
  • 好的,这里有一些示例数据。我确定我这里的设计很糟糕。

标签: java jpa database-design surrogate-key


【解决方案1】:

在我看来,您的设计的核心问题是代理键的使用。 不必总是为表创建数字单列键。 即使你这样做了,这也不能保证你不会有重复。 实际上,这会强制系统在您的表上保留更多索引,即 一项额外的工作要做。

  1. 一些概念:

    • 我使用单数作为表名,'因为表中的每个元组代表 关系的一个对象;
    • 我使用小写字母作为表名和列名(标识符)和 关键字的大写字母。我不喜欢数据库中的 CamelCase;
    • 我将<table_name>_id 用于PK 列。也有可能是单数 表名;
    • 我对我创建的所有约束和索引使用前缀 + 表名 + 详细信息。

    您不必遵循它们,但坚持一致的命名会非常好 设计模式。

  2. 我会从 property_type 字典开始:

    CREATE TABLE property_type (
        property_type   varchar(20) NOT NULL,
        CONSTRAINT p_property_type PRIMARY KEY (property_type)
    );
    

    这是一个单列表,它的存在只是为了提供一个可能的域 属性类型的值。我用了varchar(20),文字栏很漂亮 美好的。这样做的好处——您不必通过数字连接回该表 获取property_type_id=123 含义的密钥。

  3. 属性:

    CREATE TABLE property (
        property_id     integer     NOT NULL,
        property_name   varchar(50) NOT NULL,
        property_type   varchar(20) NOT NULL,
        CONSTRAINT p_property PRIMARY KEY (property_id),
        CONSTRAINT u_property_name UNIQUE (property_name),
        CONSTRAINT f_property_type FOREIGN KEY (property_type)
          REFERENCES property_type ON UPDATE CASCADE
    );
    

    我决定在这里使用数字 PK,因为我想您可能想要重命名属性 在某一点。如果您更改property_type,更新将被级联。

    尽管这里已经有一个 PK,但 UNIQUE 对名称的约束在这里是必须的, 否则,您可能会遇到具有相同名称属性的情况 具有不同的 ID。

  4. DT 表:

    CREATE TABLE dt (
        dt_id       integer     NOT NULL,
        dt_name     varchar(50) NOT NULL,
        CONSTRAINT p_dt PRIMARY KEY (dt_id),
        CONSTRAINT u_dt_name UNIQUE (dt_name)
    );
    

    同样,只有 PK 是不够的,还要创建 UNIQUE 约束。 虽然我非常想完全摆脱dt_id 并且只会保留 dt_name 本来可以PK的。

  5. DT 的属性:

    CREATE TABLE dt_property (
        dt_id           integer     NOT NULL,
        property_id     integer     NOT NULL,
        initial_value   varchar(50) NOT NULL,
        CONSTRAINT p_dt_property PRIMARY KEY (dt_id, property_id),
        CONSTRAINT f_dt_id FOREIGN KEY (dt_id) REFERENCES dt,
        CONSTRAINT f_property_id FOREIGN KEY (property_id) REFERENCES property
    );
    

    这是与您的设计的第一个重大区别——使用了复合键。

    是的,这意味着您必须随时执行 2 列 请参阅此表中的条目。但这没什么大不了的,真的——你 设计表一次,您也编写一次查询,但您的软件 如果操作得当且易于维护,可能会使用相当长的一段时间。 最好花更多时间编写查询并轻松维护 长期系统。

  6. 标签:

    CREATE TABLE tag (
        tag_id      integer     NOT NULL,
        tag_name    varchar(50) NOT NULL,
        CONSTRAINT p_tag PRIMARY KEY (tag_id),
        CONSTRAINT u_tag_name UNIQUE (tag_name)
    );
    

    这只是另一本字典。同样,就像dt 表一样,我真的很想要 避免使用tag_id 列,只保留tag_name,使其也成为PK。

  7. 新表tag_dt介绍:

    CREATE TABLE tag_dt (
        tag_id      integer     NOT NULL,
        dt_id       integer     NOT NULL,
        CONSTRAINT p_tag_dt PRIMARY KEY (tag_id, dt_id),
        CONSTRAINT f_tag_id FOREIGN KEY (tag_id) REFERENCES tag,
        CONSTRAINT f_dt_id FOREIGN KEY (dt_id) REFERENCES dt
    );
    

    创建dt + tag 关系需要此表。没有它,你就有数据 重复 - 你可以在你的架构上看到它,你有 2 行 Tag_name='Tag1'

  8. 最后,标签属性:

    CREATE TABLE tag_property (
        tag_id      integer     NOT NULL,
        dt_id       integer     NOT NULL,
        property_id integer     NOT NULL,
        a_value     varchar(50) NOT NULL,
        CONSTRAINT p_tag_property PRIMARY KEY (tag_id, dt_id),
        CONSTRAINT u_tag_property UNIQUE (tag_id, property_id),
        CONSTRAINT f_tag_property_tag FOREIGN KEY (tag_id, dt_id) REFERENCES tag_dt,
        CONSTRAINT f_tag_property_property FOREIGN KEY (dt_id, property_id)
          REFERENCES dt_property
    );
    

    此表是完整的复合键,它符合您的所有要求。 主键是tag_id, dt_id,它也是tag_dt表的外键, 所以你希望能够引入一些以前没有定义的东西。 接下来,tag_id, property_id 是唯一的,这意味着标签的属性不能重复。 最后,dt_id, property_id 引用了dt_property 表,这意味着 只有dt 允许的属性才会被注册。

最后的笔记

在大多数 DBMS 中,所有主键和唯一键都是通过索引实现的。此外,一些 DBMS 即使省略了键的第一列,也可以使用复合索引(多列索引)。 至少PostgreSQL可以做到,这是我用得最多的。

请检查您的 dttag 表,我强烈建议您摆脱代理 输入这些键,就像 property_type 完成一样。

我没有创建任何额外的索引,通常我在实现数据模型并对其进行一些实际查询之后进行此练习。

另外,不要使用valuename 作为列名。这些是保留字,您可能会在您选择的 DBMS 的未来版本中产生意想不到的影响。

【讨论】:

  • 感谢@vyegorov,我不了解其他 property_type 表。在属性表条目是这样的:id=1,property_name=size,property_type=String; id=2, property_name=length, property_type=Long;
  • @sina,这是正确的。 property_type 是为了限制您可以使用的可能类型,以便 property 中的值不会最终变得一团糟。一些 DBMS 提供 enum 类型,也是可能的,但在我看来,单独的表是更好的选择。
【解决方案2】:

数据库设计完全没问题。

只需使用 Tag_DT 和 DT_Property 表

下面是一个示例查询

select a.id_tag,b.id_Property
from 
tag_dt a,dt_property b
where a.id_dt = b.id_dt;

【讨论】:

    猜你喜欢
    • 2017-06-23
    • 2016-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-06
    • 2022-01-18
    • 1970-01-01
    相关资源
    最近更新 更多