【问题标题】:Faster insertion into two related tables更快地插入两个相关表
【发布时间】:2015-12-01 10:47:58
【问题描述】:

我的设备不断发送一些数据,我将其保存到数据库中。我正在寻找快速的方法。现在数据被发送到 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。

另一个选项是从时间和值生成一个哈希值,它可以插入到数据附件和数据中。然后可以在 Data 和 DataAttachment 中插入多个数据而不会出现问题,然后我只需通过哈希值搜索 Data 来更改 DataAttachment 中的 IDData。

我想请教一下如何快速插入。

【问题讨论】:

  • 您可以从设备本身同步数据。添加新列,其中将同步 DataAttachment 之间的 ID。对于更胖的插入,您可以使用 2 个临时表。您可以尝试使用 BULK insert(虽然我不知道您是否可以在您的设备上使用它)。之后使用验证程序,您可以快速插入主表。
  • 逐行插入是非常缓慢的过程。您必须尝试使用​​基于集合的方法(SQL 数据库的原生方法)。添加有关您正在使用的设备的更多详细信息 - 类型、操作系统等。
  • 你需要一个外键。

标签: sql-server database auto-increment sql-insert


【解决方案1】:

为此,您需要一个外键。

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。

执行此操作的典型方法是使用为此目的设计的uniqueidentifier(一个 GUID)。然后你可以在客户端分配它们,这样你就可以提前知道它们是独一无二的。另一种方法是使用随机的 64 位整数(这是 Oracle 通常用于此目的的)。

第二种方法是在单个消息中传递所有数据(无论是单个存储过程调用,还是单个 MSMQ 消息或其他)。存储过程(或任何处理消息的程序)然后可以分配唯一标识符,执行所有插入,并将分配的 ID 返回给调用者。

【讨论】:

    【解决方案2】:

    如果您使用支持表值参数的客户端 API,则可以使用以下示例中的技术在一次往返中插入数据行和相关的数据附件。在 .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
    

    【讨论】:

      【解决方案3】:

      您的场景是使用序列对象(SQLServer 2012 及更高版本)的完美案例。我知道,因为我已经将它用于您想要做的事情 - 直到表名和 DDL 非常相似。

      序列对象允许您创建唯一 ID,用于 dbo.Data 表的 ID 列和 dbo.DataAttachment 表中的相关 IDData(外键)列。它避免了在将相关附件插入 dbo.DataAttachment 之前等待 dbo.Data 插入发生。它可以让您避免使用 GUIDS 及其相关问题。并且它允许轻松批处理插入以提高性能。

      这是关于序列对象的一个​​很好的链接: https://msdn.microsoft.com/en-us/library/ff878091.aspx

      一个基本的方法是创建一个存储过程:

      1. 查找要插入的所有新数据记录(从您的一个 msmq 表中猜测),然后将它们插入到临时表中。在临时表上,您应该有一个 ID 列来获取 [your_sequence_object] 的 NEXT VALUE。该 ID 将是要插入的新数据行的主键。您需要禁用 Data 表中此列的 Identity 属性。

      2. 接下来,创建另一个临时表,用于保存每个数据行的相关附件。创建此临时表时,您将连接在上述步骤 1 中创建的数据临时表和您从中检索 DataAttachment 行的任何来源。这将允许您将步骤 1 数据临时表中的 ID 列用于 DataAttachment 中的外键列 IDData。

      对于 DataAttachment 表的 ID 列(您在此表上的主键),您可以将其保留为保留 Identity 属性,在这种情况下,您可以将其保留在 DataAttachment 临时表之外并且不明确插入它。或者,您可以使用不同的序列对象为其生成唯一值。

      1. 将Data临时表的内容插入dbo.Data表,然后将DataAttachment临时表的内容插入dbo.DataAttachment表。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-21
        • 1970-01-01
        • 2022-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多