我有持续发送一些数据的设备,我将它保存到数据库中。我正在寻找快速的方法。现在数据被发送到msmq,然后我正在进行多次插入,但这需要很长时间。我正在等待插入数据记录,获取ID,然后我在DataAttachment中插入相关记录。架构如下所示:
CREATE TABLE [dbo].[Data](
[ID] [int] IDENTITY(1,1) NOT NULL,
[IDDevice] [int] NOT NULL,
[Time] [datetime] NOT NULL,
[Value] [varchar](20) NOT NULL,
...
)
CREATE TABLE [dbo].[DataAttachment](
[IDData] [int] IDENTITY(1,1) NOT NULL,
[AttachmentType] [int] NOT NULL,
[FileName] [datetime] NOT NULL,
[FileContent] [varchar](20) NOT NULL
)
我正在考虑准备多个插入,我会在每个插入的数据后获得最后插入的id,并使用它来插入附件。但我认为这不是一个好主意,如果已经插入另一个表,我会得到错误的ID。
另一种选择是从Time和Value生成一个哈希值,该哈希值可以插入DataAttachment和Data中。然后就可以在Data和DataAttachment中插入多个数据而不会出现问题,然后我只需通过哈希值搜索数据来更改DataAttachment中的IDData。
我想请教一个如何快速插入的建议。
答案 0 :(得分:1)
您的场景是使用Sequence对象(SQLServer 2012及更高版本)的完美案例。我知道,因为我已经将它用于你正在尝试做的事情 - 直到表名和DDL非常相似。
序列对象允许您创建唯一ID,用于dbo.Data表的ID列和dbo.DataAttachment表中的相关IDData(外键)列。它避免在将相关附件插入dbo.DataAttachment之前等待dbo.Data插入。它可以让您避免使用GUIDS及其相关问题。它允许轻松批量插入以加快性能。
这里是Sequence对象的一个很棒的链接: https://msdn.microsoft.com/en-us/library/ff878091.aspx
基本方法是创建一个:
查找要插入的所有新数据记录(从您的某个msmq表中猜测),然后将它们插入临时表。在临时表上,您应该有一个ID列,用于获取[your_sequence_object]的NEXT VALUE。该ID将是插入新数据行的主键。您需要在数据表中禁用此列的Identity属性。
接下来,创建另一个临时表,该表将保存每个数据行的相关附件。创建此临时表时,您将加入上面步骤1中创建的数据临时表和您从中检索DataAttachment行的任何源。这将允许您使用步骤1数据临时表中的ID列作为DataAttachment中的外键列IDData。
对于DataAttachment表的ID列(此表上的主键),您可以将其保留为原样,并保留Identity属性,在这种情况下,您将其保留在DataAttachment临时表之外,而不是显式插入它。或者,您可以使用不同的序列对象为其生成唯一值。
答案 1 :(得分:0)
你需要一个外键。
CREATE TABLE [dbo].[DataAttachment](
[ID] [int] IDENTITY(1,1) NOT NULL,
[IDData] int FOREIGN KEY REFERENCES dbo.Data(ID),
[AttachmentType] [int] NOT NULL,
[FileName] [datetime] NOT NULL,
[FileContent] [varchar](20) NOT NULL
)
您正确地注意到,在知道ID之前,您无法创建外键。
您有两种选择。
一种是使用可以在客户端选择的ID(唯一密钥)的数据类型。为此,您需要一种方法让客户端生成唯一的ID,该ID不会与已存储在服务器上的(未知)值冲突。
执行此操作的典型方法是使用专为此目的而设计的uniqueidentifier
(GUID)。然后您可以在客户端分配它们,这样您就可以提前知道它们是唯一的。另一种方法是使用随机的64位整数(这通常是Oracle用于此目的)。
第二种方法是在单个消息中传递所有数据(无论是单个存储过程调用,还是单个MSMQ消息或其他)。然后,存储过程(或消息的任何进程)可以分配唯一标识符,执行所有插入,并将分配的ID返回给调用者。
答案 2 :(得分:0)
如果您使用的是支持表值参数的客户端API,则可以使用以下示例中的技术在单个往返中插入数据行和相关的DataAttachments。在.NET中,TVP行可以作为DataTable,DataRow数组或SqlDataRecord类型的IEnumrable传递。有关示例C#代码,请参阅http://www.dbdelta.com/maximizing-performance-with-table-valued-parameters/。
如果在客户端分配密钥以关联Data和DataAttachment行,则此技术也可以扩展为插入多个Data行。然后,您可以将两者都作为TVP传递。相关键需要在两种TVP类型中都是列,但不一定存储在表中。
CREATE TYPE DataAttachmentType AS TABLE(
[AttachmentType] [int] NOT NULL,
[FileName] [datetime] NOT NULL,
[FileContent] [varchar](20) NOT NULL
);
GO
CREATE PROC dbo.InsertDataAndAttachments
@IDDevice int
, @Time datetime
, @Value varchar(20)
, @DataAttachments dbo.DataAttachmentType READONLY
AS
SET NOCOUNT, XACT_ABORT ON;
DECLARE @IDData int;
BEGIN TRY
BEGIN TRAN;
INSERT INTO dbo.Data
( IDDevice, Time, Value )
VALUES ( @IDDevice, @Time, @Value );
SET @IDData = SCOPE_IDENTITY();
INSERT INTO dbo.DataAttachment
( IDData
, AttachmentType
, FileName
, FileContent
)
SELECT @IDData
, AttachmentType
, FileName
, FileContent
FROM @DataAttachments;
COMMIT;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK;
THROW;
END CATCH;
GO