SQL Table 相同的设计 Tables/Classes

SQL Table Design for Identical Tables/Classes

在我的代码中,我通过从一个基础 Class 'ProjectAttribute' 中创建 5 个 类 来区分 'describe' 一个项目的 5 种属性类型。一个项目可以有 0 到 x(所以在 SQL 多对多)的每种类型的属性。
这里有一些伪代码:

public abstract class ProjectAttribute
{
    public int ID { get; set; }
    public string Title { get; set; }
}
public class Attribute_HW : ProjectAttribute
{
    public static List<Attribute_HW> Hardware_Attributes { get; private set; }
    static Attribute_HW() 
    {
        // Read Attributes from external DataSource to List 'Hardware_Attributes'
    }

    // Some Attribute specific logic but no more fields
}
// Followed by 4 more Attribute Classes

public class Project
{
    public ICollection<Attribute_HW> AttributesHardware { get; set; }
    public ICollection<Attribute_SW> AttributesSoftware { get; set; }
    // 3 more Attribute Collections
}

每个 类 都有一组不同的数据,我想将其存储在我的 SQL 数据库中。但是为这样相同的 类 创建 5 table 感觉有点不对,这将导致另外 5 Link Table 到 link 到另一个 table(多对多)。最后我想出了 2 个不同的想法

想法 1
我创建了 5 tables 和 5 Link Tables 到 link 它到 'project' table.
这感觉不对,因为每个属性 table 将包含大约 20-30 个条目,我必须查询 5 个不同的 Link table 来解决这个 'mess'。

想法 2
我创建了 1 个名为 'ProjectAttrbiutes' 的 Table 并为每个属性类型添加了一个额外的列。 IE。 'IsSWAttribute'、'IsHardwareAttrobite'。另外我只加了一个 Link Table.
听起来更好,但是否建议将原始数据结构缩减回一个 table?
另一方面,我最终只会得到一个 Link table 来查询。

希望你能给我指明正确的方向。

为此我将使用 4 tables。

一个 table 包含项目。

一个 table 包含属性。

一个 table 包含属性类型。

链接项目和属性的table

[Project] -> [ProjectAttribute] <- [Attribute] -> [attributeType]

对于属性类型,在 MySQL 和 PgSQL 中有很好的类型,即枚举类型。在 MS SQL 中,您可以构建一个常规 table.

SQL Fiddle

MS SQL Server 2017 架构设置:

CREATE TABLE project
(
  id INT IDENTITY PRIMARY KEY NOT NULL,
  name VARCHAR(255)
);

CREATE TABLE attributeType
(
  id INT IDENTITY PRIMARY KEY NOT NULL,
  name VARCHAR(255)
);

CREATE TABLE attribute
(
  id INT IDENTITY PRIMARY KEY NOT NULL,
  title VARCHAR(255),
  type_id INT,
  CONSTRAINT FK_attributeType FOREIGN KEY (type_id)
    REFERENCES attributeType(id)
);

CREATE TABLE ProjectAttribute
(
  project_id INT NOT NULL,
  attribute_id INT NOT NULL,
  CONSTRAINT FK_project FOREIGN KEY (project_id)
    REFERENCES project(id),
  CONSTRAINT FK_attribute FOREIGN KEY (attribute_id)
    REFERENCES attribute(id),
  CONSTRAINT pk_ProjectAttribute PRIMARY KEY (project_id, attribute_id)
);

INSERT INTO project (name) VALUES ('project 1 with hardware and software');
INSERT INTO project (name) VALUES ('project 2 with hardware only');
INSERT INTO project (name) VALUES ('project 3 with software only');

INSERT INTO attributeType (name) VALUES ('Hardware');
INSERT INTO attributeType (name) VALUES ('Software');

INSERT INTO attribute (title, type_id) VALUES ('Some hardware 1', 1);
INSERT INTO attribute (title, type_id) VALUES ('Some hardware 2', 1);
INSERT INTO attribute (title, type_id) VALUES ('Some software 1', 2);
INSERT INTO attribute (title, type_id) VALUES ('Some software 2', 2);
INSERT INTO attribute (title, type_id) VALUES ('Some hardware 3', 1);

INSERT INTO ProjectAttribute VALUES (1, 1);
INSERT INTO ProjectAttribute VALUES (1, 2);
INSERT INTO ProjectAttribute VALUES (1, 3);
INSERT INTO ProjectAttribute VALUES (1, 4);
INSERT INTO ProjectAttribute VALUES (2, 1);
INSERT INTO ProjectAttribute VALUES (2, 5);
INSERT INTO ProjectAttribute VALUES (3, 3);

查询 1:

SELECT * FROM project p
INNER JOIN ProjectAttribute pa
  ON p.id = pa.project_id
INNER JOIN attribute a
  ON a.id = pa.attribute_id
INNER JOIN attributeType t
  ON t.id = a.type_id

Results:

| id |                                 name | project_id | attribute_id | id |           title | type_id | id |     name |
|----|--------------------------------------|------------|--------------|----|-----------------|---------|----|----------|
|  1 | project 1 with hardware and software |          1 |            1 |  1 | Some hardware 1 |       1 |  1 | Hardware |
|  1 | project 1 with hardware and software |          1 |            2 |  2 | Some hardware 2 |       1 |  1 | Hardware |
|  1 | project 1 with hardware and software |          1 |            3 |  3 | Some software 1 |       2 |  2 | Software |
|  1 | project 1 with hardware and software |          1 |            4 |  4 | Some software 2 |       2 |  2 | Software |
|  2 |         project 2 with hardware only |          2 |            1 |  1 | Some hardware 1 |       1 |  1 | Hardware |
|  2 |         project 2 with hardware only |          2 |            5 |  5 | Some hardware 3 |       1 |  1 | Hardware |
|  3 |         project 3 with software only |          3 |            3 |  3 | Some software 1 |       2 |  2 | Software |

这是我的一个 table 建议(类型是一个枚举,如 {Hardware, Software, ...} - 这意味着 Types table in MS SQL with id , 类型):

类型:ID、类型 属性:Id、标题、TypeId Project_Attributes: Project_Id, AttributeId (Bridge table for many-to-many) 项目:ID、标题、...

其实一图千言,即使是短时间写的代码(所以需要经过深思熟虑修改)也会给出更多的想法。这是代码优先模型,因此您可以 运行 并让它为您创建 table:

string defaultConString = 
@"server=.\SQLExpress;Database=SampleDb;Trusted_Connection=yes;";

void Main()
{
    CreateData();
    // check db state
    ListData();
    // Done with sample database. Delete.
    // new SampleContext(defaultConString).Database.Delete();
}

private void CreateData()
{
    var db = new SampleContext(defaultConString);
    if (db.Database.CreateIfNotExists())
    {

        // Create some types
        var tHW = new Type { Name = "Hardware" };
        var tSW = new Type { Name = "Software" };
        var tFW = new Type { Name = "Firmware" };
        db.Types.AddRange(new Type[] {tHW,tSW,tFW});
        db.SaveChanges();

        // create some Attributes using types above
        var a1 = new Attribute
        {
            Name = "Macbook Pro",
            Type = tHW,
            AttrProperties = new List<AttrProperty> {
                new AttrProperty{ Description=@"15"" retina" },
                new AttrProperty{ Description=@"i9-8950HK" },
                new AttrProperty{ Description=@"32Gb DDR4 RAM" },
                new AttrProperty{ Description=@"512Gb SSD" },
                new AttrProperty{ Description=@"AMD Radeon Pro 555" },
                new AttrProperty{ Description=@"OSX Mojave 10.14" },
                }
        };
        var a2 = new Attribute
        {
            Name = "iMac",
            Type = tHW,
            AttrProperties = new List<AttrProperty> {
                new AttrProperty{ Description=@"27"" retina 5K" },
                new AttrProperty{ Description=@"i5 3.4Ghz" },
                new AttrProperty{ Description=@"40Gb DDR4 RAM" },
                new AttrProperty{ Description=@"1Tb Fusion" },
                new AttrProperty{ Description=@"AMD Radeon Pro 570" },
                new AttrProperty{ Description=@"OSX Mojave 10.14" },
                }
        };
        var a3 = new Attribute
        {
            Name = "PC",
            Type = tHW,
            AttrProperties = new List<AttrProperty> {
                new AttrProperty{ Description=@"AMD Ryzen Threadripper 1950x" },
                new AttrProperty{ Description=@"64Gb RAM" },
                new AttrProperty{ Description=@"1 Tb 7400 RPM" },
                new AttrProperty{ Description=@"512Gb M2 mSATA" },
                new AttrProperty{ Description=@"AMD Radeon Pro 570" },
                new AttrProperty{ Description=@"Linux-Debian 9.5" },
                }
        };

        var a4 = new Attribute
        {
            Name = "Database",
            Type = tSW,
            AttrProperties = new List<AttrProperty> {
                new AttrProperty{ Description=@"postgreSQL" },
                new AttrProperty{ Description=@"11 (beta)" },
                }
        };

        var a5 = new Attribute
        {
            Name = "SomeROM",
            Type = tFW,
            AttrProperties = new List<AttrProperty> {
                new AttrProperty{ Description=@"SomeROM update" },
                new AttrProperty{ Description=@"Some version" },
                }
        };

        db.Attributes.AddRange(new Attribute[] {a1,a2,a3,a4,a5});

        // Some projects using those

        var p1 = new Project
        {
            Name = "P1",
            StartDate = new DateTime(2018, 1, 1),
            Attributes = new List<Attribute>() { a1, a2, a4 }
        };
        var p2 = new Project
        {
            Name = "P2",
            StartDate = new DateTime(2018, 1, 1),
            Attributes = new List<Attribute>() { a1, a3, a5 }
        };
        var p3 = new Project
        {
            Name = "P3",
            StartDate = new DateTime(2018, 1, 1),
            Attributes = new List<Attribute>() { a2, a3, a4, a5 }
        };

        db.Projects.AddRange(new Project[] { p1,p2,p3 });
        db.SaveChanges();
    }
}

private void ListData()
{
    var db = new SampleContext(defaultConString);
//  db.Database.Log =Console.Write;
    foreach (var p in db.Projects.Include("Attributes").ToList())
    {
        Console.WriteLine($"Project {p.Name}, started on {p.StartDate}. Has Attributes:");
        foreach (var a in p.Attributes)
        {
            Console.WriteLine($"\t{a.Name} [{a.Type.Name}] ({string.Join(",",a.AttrProperties.Select(ap => ap.Description))})");
        }
    }
}

public class Type
{
    public int TypeId { get; set; }
    public string Name { get; set; }

    public virtual List<Attribute> Attributes { get; set; }
    public Type()
    {
        Attributes = new List<Attribute>();
    }
}

public class Attribute
{
    public int AttributeId { get; set; }
    public string Name { get; set; }
    public int TypeId { get; set; }
    public virtual Type Type { get; set; }
    public virtual List<Project> Projects { get; set; }
    public virtual List<AttrProperty> AttrProperties { get; set; }
    public Attribute()
    {
        Projects = new List<Project>();
    }
}

public class AttrProperty
{ 
    public int AttrPropertyId { get; set; }
    public string Description { get; set; }
    public virtual Attribute Attribute {get;set;}
}

public class Project
{
    public int ProjectId { get; set; }
    public string Name { get; set; }
    public DateTime StartDate { get; set; }
    public virtual List<Attribute> Attributes {get;set;}

    public Project()
    {
        Attributes = new List<Attribute>();
    }
}


public class SampleContext : DbContext
{
    public SampleContext(string connectionString) : base(connectionString) { }
    public DbSet<Type> Types { get; set; }
    public DbSet<Attribute> Attributes { get; set; }
    public DbSet<Project> Projects { get; set; }
}

如果那不是一个多用户的大项目,那么看看 LiteDB。这可能正是您正在寻找的东西,即使不是几分钟,也可以在几小时内学会。