Дизайн таблиц SQL для одинаковых таблиц / классов - PullRequest
0 голосов
/ 02 октября 2018

В моем коде я различаю 5 типов атрибутов, которые «описывают» проект, создавая 5 классов, наследуемых от одного базового класса «ProjectAttribute».Проект может иметь от 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 таблиц для таких идентичных классов, что приведет к появлению еще 5 таблиц ссылок, чтобы связать их с другой таблицей (многие ко многим).Наконец, я пришел к двум различным идеям

Идея 1
Я создаю 5 таблиц и 5 таблиц ссылок, чтобы связать их с таблицей 'проекта'.
Это неправильнопотому что каждая таблица атрибутов будет содержать около 20-30 записей, и мне нужно запросить 5 разных таблиц ссылок, чтобы устранить этот «беспорядок».

Идея 2
Я создаю 1 таблицу с именем «ProjectAttrbiutes» и добавляю один дополнительный столбец для каждого типа атрибута.Т.е. «IsSWAttribute», «IsHardwareAttrobite».Кроме того, я добавляю только одну таблицу ссылок.
Звучит лучше, но желательно ли уменьшить исходную структуру данных обратно в одну таблицу?
С другой стороны, я бы в итоге запросил только одну таблицу ссылок.

Надеюсь, вы можете указать мне правильное направление.

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

Вот мое предложение по одной таблице (тип - это перечисление типа {Hardware, Software, ...} - это означало бы таблицу типов в MS SQL с идентификатором Type):

Types: Id,Атрибут типа: Id, Title, TypeId Project_Attributes: Project_Id, AttributeId (таблица моста для многих ко многим) Проекты: Id, Title, ...

На самом деле картинка стоит тысячи слов, даже этот код написанв течение короткого времени (поэтому необходимо пересмотреть после некоторой мысли) даст больше идей.Это модель кода в первую очередь, так что вы можете запустить и позволить ей создавать таблицы для вас:

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.Это может быть то, что вы ищете и можете узнать за часы, если не за минуты.

0 голосов
/ 02 октября 2018

Для этой цели я бы использовал 4 таблицы.

Таблица, содержащая проекты.

Таблица, содержащая атрибуты.

Таблица, содержащая типы атрибутов.

Таблица, которая связывает проект и атрибуты

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

Длятипы атрибутов, для этого есть хорошие типы в MySQL и PgSQL, тип enum.В MS SQL вы можете создать обычную таблицу.

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

Результаты :

| 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 |
...