将排序数据插入到具有非聚簇索引的表中

时间:2014-12-30 08:14:20

标签: sql sql-server

我的数据库架构:

  • pointpoint_id int PK, name varchar);
  • point_logpoint_log_id int PK, point_id int FK, timestamp datetime, value int

point_log有一个索引:

point_log_idx1 (point_id asc, timestamp asc)

我需要将点日志样本插入到point_log表中,在每个事务中只插入一个point_id的日志样本,并且日志样本已经按升序排序。这意味着事务中的所有日志样本数据与index( point_log_idx1)的顺序相同,如何使SQL Server利用这一点,以避免树搜索成本?

2 个答案:

答案 0 :(得分:0)

这似乎是一个很好的机会,可以通过其父Point_Log外键将point_id上的聚簇索引更改为群集:

CREATE TABLE Point_log
( 
    point_log_id int PRIMARY KEY NONCLUSTERED, 
    point_id int, 
    timestamp datetime, 
    value int
);

然后:

CREATE CLUSTERED INDEX C_PointLog ON dbo.Point_log(point_id);

基本原理:在为给定point_log

获取point_log条记录时,这会减少pointid上的读取IO

此外,鉴于Sql Server将向非唯一聚簇索引添加4字节uniquifier,您也可以在群集中包含Surrogate PK,以使其唯一,即:

CREATE UNIQUE CLUSTERED INDEX C_PointLog ON dbo.Point_log(point_id, point_log_id);

如果每point_log_idx1 ( point_id asc, timestamp asc)point_logspoint,则需要保留非聚集索引point_log.pointid,并且假设对point_log.timestamp&放置的查询具有良好的选择性; {{1}}

答案 1 :(得分:0)

与物理写入磁盘和页面拆分和日志记录的成本相比,树搜索成本可能微不足道。

1)你绝对应该批量插入数据,而不是逐行插入数据。

2)为了减少point_log_idx1索引的页面拆分,您可以尝试在ORDER BY语句中使用INSERT。它仍然不能保证磁盘上的物理顺序,但它确实保证了生成point_log_id IDENTITY的顺序,并且希望它将提示按此顺序处理源数据。如果以请求的顺序处理源数据,则point_log_idx1索引的b树结构可能会增长而不会产生不必要的高成本页面拆分。

我使用的是SQL Server 2008.我的系统可以24/7全天候收集中央数据库中的大量监控数据。最初我是在逐行到达时插入数据。然后我意识到每个插入都是一个单独的事务,大部分时间系统都花在写入事务日志上。

最后,我使用接受表值参数的存储过程批量插入数据。在我的情况下,批次是几百到几千行。在我的系统中,我只保留给定天数的数据,所以我经常删除过时的数据。为了保持系统性能稳定,我还要定期重建索引。

在您的示例中,它可能如下所示。

首先,创建一个表类型:

CREATE TYPE [dbo].[PointValuesTableType] AS TABLE(
    point_id int,
    timestamp datetime,
    value int
)

然后程序看起来像这样:

CREATE PROCEDURE [dbo].[InsertPointValues]
    -- Add the parameters for the stored procedure here
    @ParamRows dbo.PointValuesTableType READONLY
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    BEGIN TRANSACTION;
    BEGIN TRY

        INSERT INTO dbo.point_log
            (point_id
            ,timestamp
            ,value)
        SELECT
            TT.point_id
            ,TT.timestamp
            ,TT.value
        FROM @ParamRows AS TT
        ORDER BY TT.point_id, TT.timestamp;

        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
    END CATCH;

END

在实践中,您应该使用ORDER BY或不使用INSERT来衡量系统的效率。 您确实需要考虑INSERT操作的性能以及后续查询的性能。

可能更快的插入会导致索引碎片更高,从而导致查询速度变慢。

因此,您应该在ORDER BY之后使用{{1}}或不使用{{1}}来检查索引的碎片。 您可以使用sys.dm_db_index_physical_stats获取索引统计信息。

  

返回数据和索引的大小和碎片信息   SQL Server中指定的表或视图。