微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 SQL Server 数据库中存储列表 动物桌疫苗表动物疫苗表SQL

如何解决如何在 SQL Server 数据库中存储列表 动物桌疫苗表动物疫苗表SQL

我对数据库很陌生,我一直在用 C# 开发一个学校的 windows 窗体 .NET 项目,但我遇到了一个问题。

我在 sql Server 数据库中有一个包含患者(动物)的表格,我需要添加每个患者接种的疫苗剂量列表(长度未知)。每只动物在其疫苗列表中都有不同的长度和值。

谁能告诉我如何将列表存储在数据库列中?

解决方法

关系数据库设计的一个核心原则(database normalization process)是一列应该包含原子数据。

不是在一列中存储多个值(疫苗剂量),而是将它们作为行存储在单独的相关表(动物外键)中。这将本质上提供一个大小不一且不受限制的列表。

如果您有不同类型的疫苗,您可能还应该有额外的表格。

,

您不想在一列中存储多个值。这不是 SQLish 做事的方式。

相反,您需要多个表。可能是这样的:

create table animals (
    animal_id int identity(1,1) primary key,. . .    -- other information about the animal
);

create table vaccinations v (
    vaccination_id int identity(1,animal_id int not null references animals(animal_id),vaccination_name varchar(255),dosage varchar(255),given_at datetime,. . .   -- perhaps other information
);

请注意,疫苗接种列表可能存储在单独的表格中,可能每个剂量都有单独的一行。您的问题没有足够的信息来确定是否是这种情况。

另请注意,给定疫苗接种有多项信息,例如给定日期/时间、接种疫苗的人员等。

,

动物桌

此表将存储有关动物的数据

ID animal_Name
1 辛巴
2 曼多

疫苗表

此表将存储有关疫苗的数据

ID 疫苗名称
1 疫苗1
2 疫苗2
3 疫苗3

动物疫苗表

此关系表将存储给每只动物接种的所有疫苗

animal_ID vaccine_ID given_At 剂量
1 1 15/5/2012 5-Mg
1 2 5/9/2020 9-Mg
2 1 6/1/2021 20-Mg
1 1 6/4/2021 6-Mg

从这张表中可以看出Simba打了两次vacine1,也只打了一次vacine2,而mando只打了一次vacine1

SQL

create table animals (
    ID int identity(1,animal_Name varchar(200) not null
);

create table vaccines (
    ID int identity(1,Vaccine_Name varchar(200)
);

create table Animal-vaccin(
   animal_ID int not null references animals(ID),vaccine_ID int not null references vaccines(ID),dosage varchar(200),given_At datetime

);
,

所以你有一张Animals表和一张Vaccines

class Animal
{
    public int Id {get; set;}
    public string Name {get; set;}

    ... // other properties,like BirthDate
}

class Vaccine
{
    public int Id {get; set;}
    public string Name {get; set;}

    ... // other properties
}

您给动物注射了一剂疫苗。要记住哪个动物得到了哪个剂量,您需要一个表 VacineDosages。在此表中,您可以看到哪种动物接受了哪种疫苗的剂量。您可能想知道动物获得剂量的日期,以及剂量的数量。

Animals 和 VaccineDosages 之间存在一对多的关系:每只动物都得到零个或多个 VaccineDosages,每个 VaccineDosage 都被给予了一个动物

同样,疫苗和疫苗剂量之间也存在一对多的关系:在疫苗剂量中,每种疫苗都使用了零次或多次,每个疫苗剂量都是恰好一种疫苗的剂量。

在使用关系数据库时,一对多关系是使用外键实现的。 “多”侧的项目获得它所属的“一”侧的项目的外键。

每一种疫苗剂量都用于为一只动物接种疫苗;剂量“属于”这个动物,因此它获得了这个动物的外键。同样,一个 VaccineDosage 属于一个 Vaccine,因此有一个外键

class VaccineDosage
{
    public int Id {get; set;}

    // foreign keys
    public int AnimalId {get; set;}
    public int VaccineId {get; set;}

    ... // other properties,like Vaccination Date and Amount
}

现在要添加 VaccineDosages,您需要知道 VaccineDosage 被给予了哪种动物,以及哪种疫苗。好吧,您不需要知道 Animal 和 Vaccine,只需知道它们的 ID。

假设您有一系列尚未添加到数据库中的 VaccineDosage,因此它们还没有 Id。

Id  AnimalId VaccineId  Amount     Date
 0     10       23        10    2021-05-10
 0     10       24         5    2021-05-10
 0     12       23        10    2021-05-09
 0     13       23        10    2021-05-10
 etc.

或者,在 C# 类中:

IEnumerable<VaccineDosage> vaccineDosages = new []
{
    new VaccineDosage
    {
         AnimalId = 10,VaccineId = 23,Amount = 10,Date = new DateTime(2021,05,10),},new VaccineDosage
    {
         AnimalId = 10,VaccineId = 24,Amount = 5,... etc
}

现在如何将这个列表添加到数据库中?该方法取决于您使用什么方法与数据库通信:

  • LOW 级别:使用SQL,将values参数一一添加
  • 中级:使用nuget包DAPPER:你只需要提供SQL和对象
  • 高级:使用实体框架。

当然,作为一名优秀的程序员,您想隐藏保存数据的位置和方式:它可以在数据库中,但也许您想在用于单元测试的 XML 文件中进行,例如 CSV、Json?

所有用户想知道的:我想将项目放入一个对象中,然后,即使在重新启动计算机后,我也可以再次从同一个对象中获取项目。

这个“仓库”的东西,通常被称为存储库:

class Repository
{
    public int AddAnimal(Animal animal} {...}
    public int AddVaccine(Vaccine vaccine {...}
    public int AddDosage(VaccineDosage dosage) {...}

这些方法返回添加项的新创建的Id,以便稍后您可以通过Id获取它们:

    public Animal FetchAnimal(int animalId) {...}

您可以根据需要向存储库添加方法:

    public IEnumerable<Animal> FetchAllAnimals() {...}
    public IEnumerable<Animal> FetchAnimalsOfOwner(int ownerId) {...}
    public IEnumerable<Animal> FetchUnvaccinateAnimals(int vaccineId) {...}

在您的情况下,您还需要一种添加疫苗剂量序列的方法。

public void AddDosages(IEnumerable<VaccineDosage> dosages) {...}

也许所有疫苗剂量都插入到同一个动物中,或者在同一个日期插入,如果是这种情况,请考虑创建重载:

public void AddDosages(int animalId,IEnumerable<Vaccine> vaccins,...)

为了让您的生活更轻松,这些重载可以调用您的原始版本。您可能不会每秒添加 1000 种疫苗。

存储库的好处在于您隐藏了数据的保存方式。对于小型项目和单元测试,您可以将其另存为 CSV 文件或 XML。稍后您可以安全地更改它以使用 SQL 保存在数据库中,您可以更改数据库的类型,或用于与 DBMS 通信的方法。您存储库的用户无需更改,因此无需再次测试。

低级:带参数的 SQL

如果你还在学习数据库,最好从低级开始,这样当你使用更高级别的方法时,你会感觉到哪些代码是在“幕后”完成的

在低级别与数据库通信时,您创建了一个数据库连接。您要求连接创建一个命令,并用 SQL 文本和参数填充该命令。最后执行命令。

通常您不希望长时间打开数据库连接。您为一个操作创建了一个打开和关闭数据库连接的过程:添加一个 VaccineDosage,或一个相当小的 VaccinedDosage 序列。

如果您需要在一次调用中将一百万个 VaccineDosages 添加到数据库,那么您就是在谈论批量使用。这可能需要不同的方法。这超出了您的问题范围。

数据库连接有一个连接字符串。这取决于您使用的数据库管理系统 (DBMS),是否需要提供此连接字符串。如果您不提供它,应用程序将使用文件 app.config 中提供的连接字符串。

要隐藏您使用的实际 DBMS,请考虑创建一个工厂方法来为您创建正确的数据库连接:

public DbConnection CreateDatabaseConnection()
{
    // for example: use SQLight as database:
    string dbConnectionString = this.CreateDbConnectionString();
    return new System.Data.SQLite.SQLiteConnection(dbConnectionString);
}

如果您以后决定使用不同的 DBMS,您只需更改此过程。

隐藏表的名称,以便您以后可以更改它们:

private const string tableNameAnimals = "Animals";
private const string tableNameVaccines = "Vaccines";
private const string tableNameVaccineDosages = "VaccineDosages";

public void Add(VaccineDosage dosage)
{
    const string sqlText = @"Insert into table " + tableNameVaccineDosages
        + " (AnimalId,VaccineId,Amount,Date)"
        + " Values(@AnimalId,@VaccineId,@Amount,@Date);"

    using (var dbConnection = this.CreateDatabaseConnection())
    {
        dbConnection.Open();
        using (var dbCommand = dbConnection.CreateDbCommand())
        {
            dbCommand.CommandText = sqlText;

            // add all parameters:
            dbCommand.Parameters.AddWithValue("@AnimalId,dosage.AnimalId);
            dbCommand.Parameters.AddWithValue("@VaccineId,dosage.VaccineId);
            ... // add the other parameters

            // Execute the command:
            dbCommand.ExecuteNonQuery();
        }
    }
}

using 语句断言所有物品都已正确关闭并丢弃,一旦不再使用

如果要添加一堆VaccineDosages,可以多次调用此方法。或者稍微优化一下,复用dbCommand

public void Add(IEnumerable<VaccineDosage> dosages)
{
    const string sqlText = @"Insert into table " + tableNameVaccineDosages
        + " (AnimalId,@Date);"

    using (var dbConnection = this.CreateDatabaseConnection())
    {
        dbConnection.Open();         
        foreach (var dosage in dosages)
        {
            using (var dbCommand = dbConnection.CreateDbCommand())
            {
                 ... etc.
            }
        }
    }
}

当然,您可以省略使用参数并创建一个包含完整 sql 命令的 sql 文本,但这可能很危险:人们可能会破坏您的数据库。见SQL Injection attack

总是尝试使用常量 sql 字符串作为文本。不要编辑 sql 字符串,要为参数添加值,请始终使用 Parameters.AddWithValue

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。