如何在SQL Server数据库中存储列表



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

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

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

关系数据库设计(数据库规范化过程)的核心原则是列应包含原子数据。

与其在一列中存储多个值(疫苗剂量),不如将这些值作为行存储在单独的相关表中(动物外键)。这将从本质上提供一个大小不一的列表。

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

动物餐桌

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

ID animal_Name
1 Simba
2 mando

您不希望在一列中存储多个值。这不是SQL式的做事方式。

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

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, 1) primary key,
animal_id int not null references animals(animal_id),
vaccination_name varchar(255),
dosage varchar(255),
given_at datetime,
. . .   -- perhaps other information
);

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

还要注意,给定的疫苗接种有多个信息项,如接种日期/时间、接种者等。

因此您有一个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表格。在这张表中,你可以看到哪只动物接种了哪种疫苗的剂量。你可能想知道动物在哪一天得到剂量,也许还有剂量的多少。

动物和疫苗剂量之间存在一对多的关系:每只动物都有零或更多的疫苗剂量,每只疫苗剂量只给了一只动物

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

当使用关系数据库时,一对多关系是使用外键实现的。";许多";side获得"上的项目的外键;一个";

每种疫苗剂量都用于为一只动物接种疫苗;剂量";属于";这只动物,因此它得到了这只动物的外键。类似地,疫苗剂量属于疫苗,因此具有外键

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
}

现在要添加疫苗剂量,您需要知道给哪只动物注射了疫苗剂量,以及使用了哪种疫苗。好吧,你不需要知道动物和疫苗,只需要他们的ID就可以了。

因此,假设您有一个尚未添加到数据库中的VaccineDosages序列,因此它们还没有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,
Date = new DateTime(2021, 05, 10),
},
... etc
}

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

  • LOW级别:使用SQL,并逐个添加值参数
  • 中级:使用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通信的方法。存储库的用户不必更改,因此不需要再次进行测试。

LOW级别:带参数的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, VaccineId, Amount, Date)"
+ " Values(@AnimalId, @VaccineId, @Amount, @Date);"
using (var dbConnection = this.CreateDatabaseConnection())
{
dbConnection.Open();         
foreach (var dosage in dosages)
{
using (var dbCommand = dbConnection.CreateDbCommand())
{
... etc.
}
}
}
}

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

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

最新更新