我有一个存储任务的应用程序,我想为这些任务添加附件。
我尝试了三种不同的方式,并且不知道它们中是否有任何一种是正确的,我正在寻找有关去哪里的建议:
例如,简化我使用了一个表:
+----------------------------------------------------------------------------+
| TaskID Description attachmentString |
+----------------------------------------------------------------------------+
| 1 Task1 "FileName1:::fileLocation;FileName2:::fileLocation" |
| 2 Task2 "FileName3:::fileLocation;FileName4:::fileLocation" |
+----------------------------------------------------------------------------+
这与配置文件数据存储在ASP.NET成员资格中的方式类似。
我也尝试过:
+---------------------------+
| TaskID Description |
+---------------------------+
| 1 Task1 |
| 2 Task2 |
+---------------------------+
+------------------------------------------------------+
| AttachmentId Description Location TaskId |
+------------------------------------------------------+
| 1 FileName1 FileLocation 1 |
| 2 FileName2 FileLocation 1 |
+------------------------------------------------------+
如果我使用第一个选项,我可以选择任务并在一次SQL调用中获取所有附件数据;但是我必须解析字符串似乎很笨拙。它也不是很“关系”
然而,使用附件ID,如果我想获取附件,我要么在attachmentId上加入两个表,然后有附件数量x返回的任务数量。我最多可以有5个附件,因此对于50个任务,它可以返回250行,其中第一列(来自JOIN的任务表一侧)被重复,这看起来像是浪费。显然我在表格中只有一点描述!!!
我还考虑过获取任务数据,然后单独获取附件数据,然后在我的应用程序中加入它们。这返回的数据少于第二个选项,但需要两次调用数据库,这似乎也是错误的。
我这样做错了吗?有没有更好的办法?有没有人对最佳方法有任何想法。
我对SQL不太自信,也许我错过了一些巨大的东西,所以任何指针都会感激不尽。
答案 0 :(得分:1)
正确的设计显然是两张桌子。只有一个表违反了第一个普通表格。
关于负载问题,两种方法都是正确的。
加入sql语句中的表是大多数ORM为急切加载相关对象所做的事情。显然有一些网络流量开销,但我认为这是可以接受的。
执行两个单独的sql语句也是正确的。您可以将它们一起发送到SQL Server以节省往返。它有一个缺点,但是你需要在客户端执行连接。
那么,您是否愿意编写更多代码来节省一些网络流量?
编辑:
鉴于以下表格和数据:
CREATE TABLE Tasks
(
TaskId int IDENTITY(1,1) PRIMARY KEY,
TaskDescription nvarchar(500) NOT NULL
)
CREATE TABLE TaskAttachments
(
AttachmentId int IDENTITY(1,1) PRIMARY KEY,
TaskId int NOT NULL REFERENCES Tasks(TaskId),
[FileName] nvarchar(500) NOT NULL,
[FileLocation] nvarchar(500) NOT NULL
)
GO
INSERT INTO Tasks VALUES
('Task1'), ('Task2')
INSERT INTO TaskAttachments VALUES
(1, 'FileName1', 'File location 1'),
(1, 'Filename2', 'File location 2'),
(2, 'FileName3', 'File location 3'),
(2, 'Filename4', 'File location 4')
以下课程:
public class TaskAttachment
{
public int AttachmentId { get; set; }
public string FileName { get; set; }
public string FileLocation { get; set; }
}
public class AppTask
{
public int TaskId { get; set; }
public string TaskDescription { get; set; }
public List<TaskAttachment> Attachments { get; set; }
public AppTask()
{
this.Attachments = new List<TaskAttachment>();
}
}
以下类通过在一个批处理中执行两个select语句来加载任务及其附件:
public class DataLayer
{
private readonly SqlConnection connection;
public DataLayer(SqlConnection connection)
{
this.connection = connection;
}
public List<AppTask> GetTasks()
{
var commandText = @"
SELECT TaskId, TaskDescription FROM Tasks;
SELECT AttachmentId, TaskId, [FileName], FileLocation FROM TaskAttachments;
";
using (var cmd = new SqlCommand(commandText, connection))
using (var reader = cmd.ExecuteReader())
{
var tasks = new List<AppTask>();
while (reader.Read())
{
var task = new AppTask
{
TaskId = reader.GetInt32(0),
TaskDescription = reader.GetString(1)
};
tasks.Add(task);
}
var taskDic = tasks.ToDictionary(x => x.TaskId);
reader.NextResult();
while (reader.Read())
{
var attachment = new TaskAttachment
{
AttachmentId = reader.GetInt32(0),
TaskId = reader.GetInt32(1),
FileName = reader.GetString(2),
FileLocation = reader.GetString(3)
};
var task = taskDic[attachment.TaskId];
task.Attachments.Add(attachment);
}
return tasks;
}
}
}
您可以像这样使用上述类:
using (var cn = new SqlConnection("Data Source=(local);Initial Catalog=Tests;Integrated Security=SSPI"))
{
var dataLayer = new DataLayer(cn);
cn.Open();
var tasks = dataLayer.GetTasks();
}