【问题标题】:Better Practice: Delete&Insert vs. Update更好的做法:删除和插入与更新
【发布时间】:2015-07-23 12:40:39
【问题描述】:

我需要从 CSV 文件中解析数据(约 60000 行)并将它们写入 MSSQL 表(数据是日期/时间和十进制数的值)。 每天我都会收到一个这样的 CSV 文件。 问题是,在我每天获取的 CSV 文件中,我有过去 5 天的数据,这意味着我有过去几天已经写过的日期的数据,但我需要用来自文件。

我正在尝试在两种方法之间做出决定: 当我获得新的 CSV 文件和 INSERT 时,批量删除我需要重写的旧数据,而不是根据日期和时间和 ID 查找每条记录并更新它。

1. 有什么更好的做法可以减少我的数据库上的碎片和维护问题?

  1. 从性能的角度来看,哪个更便宜?

如果要在两者之间进行选择,我更喜欢让我的数据库保持良好状态而不是高性能,因为无论如何文件都会在夜间写入。

编辑:如果我将添加一个维护计划,在我批量删除和插入新数据后每天重建索引,这是否足以避免碎片问题,或者有什么我遗漏的?

【问题讨论】:

  • 你考虑过“合并”吗?
  • 前 4 天的数据是否始终与您已有的数据相同?所以你实际上甚至不会更新任何东西?
  • @Dan,谢谢,我从未听说过“合并”。我现在研究了一下,它似乎可以解决问题。你知道这种日常“合并”对数据库碎片状态的影响吗?
  • @JamesZ,4天不一样,有变化的,不然显然不需要重新写数据了。
  • @LimS,我添加了一个带有MERGE 示例和碎片说明的答案。

标签: sql-server database-performance database-administration


【解决方案1】:

更快更好的方法是删除所有旧数据,在没有 SSIS 的情况下使用 SSIS 导入数据或批量插入,然后重建碎片索引。以script为例。

【讨论】:

  • 我对SSIS不熟悉,但是看到SQL WEB EDITION 2008 R2不支持,所以和我没关系
  • 你可以使用bulk insert代替SSIS,但是会慢一些。
【解决方案2】:

我将在您的 CSV 文件中插入所有数据,然后它们会删除重复数据。

以下代码可帮助您删除重复项。 希望对你有帮助:)

delete b from    your_table c join
(SELECT max(a.id) id, a.date
 FROM your_table a 

GROUP BY a.date 
having count(0) > 1
) as b
on c.date = b.date
and c.id <> b.id

【讨论】:

  • 谢谢@Liz,但是关于性能和碎片你能告诉我什么?
  • 如果用数百万条记录对此进行了测试。并且使用适当的索引需要几秒钟。关于 Fragmentation 可以给你任何意见。我不是很熟悉。
【解决方案3】:

这是一种使用带有已解析 CSV 数据的暂存表的 MERGE 技术。或者,您可以使用表值参数而不是临时表源。

关于您的碎片​​问题,这主要取决于在现有目标表日期范围内插入了多少新行。如果没有该范围内的新行,则碎片是微不足道的(低于 3%,如下面的脚本所示。如果碎片成为问题,您始终可以在 ETL 之后执行索引 REBUILDREORGANIZE

CREATE TABLE dbo.Test(
      TestDateTime datetime2(0) NOT NULL 
        CONSTRAINT PK_Test PRIMARY KEY
    , TestData int NOT NULL
    );
CREATE TABLE dbo.TestStaging(
      TestDateTime datetime2(0) NOT NULL
        CONSTRAINT PK_TestStaging PRIMARY KEY
    , TestData int NOT NULL
    );
GO

--load 10 days into main table (61710 per day)
WITH 
    t4 AS (SELECT n FROM (VALUES(0),(0),(0),(0)) t(n))
    ,t256 AS (SELECT 0 AS n FROM t4 AS a CROSS JOIN t4 AS b CROSS JOIN t4 AS c CROSS JOIN t4 AS d)
    ,t256K AS (SELECT ROW_NUMBER() OVER (ORDER BY (a.n)) - 1 AS num FROM t256 AS a CROSS JOIN t256 AS b CROSS JOIN t4 AS c)
INSERT INTO dbo.Test WITH(TABLOCKX) (TestDateTime, TestData) 
SELECT DATEADD(second, num*7, CAST('2015-07-01T00:00:00' AS datetime2(0))), num
FROM t256K
WHERE num <= 123420;
GO

--load 4 most recent days with new values plus 1 new day into staging table
WITH 
    t4 AS (SELECT n FROM (VALUES(0),(0),(0),(0)) t(n))
    ,t256 AS (SELECT 0 AS n FROM t4 AS a CROSS JOIN t4 AS b CROSS JOIN t4 AS c CROSS JOIN t4 AS d)
    ,t256K AS (SELECT ROW_NUMBER() OVER (ORDER BY (a.n)) - 1 AS num FROM t256 AS a CROSS JOIN t256 AS b CROSS JOIN t4 AS c)
INSERT INTO dbo.TestStaging WITH(TABLOCKX) (TestDateTime, TestData) 
SELECT DATEADD(second, num*7, CAST('2015-07-07T00:00:06' AS datetime2(0))), num
FROM t256K
WHERE num <= 61710;
GO

--show fragmentation before MERGE
SELECT *
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(N'dbo.Test'), NULL, NULL, 'DETAILED');
GO

MERGE dbo.Test AS target
USING dbo.TestStaging AS source ON
    source.TestDateTime = target.TestDateTime
WHEN MATCHED THEN
    UPDATE SET TestData = source.TestData
WHEN NOT MATCHED BY target THEN
    INSERT (TestDateTime, TestData) VALUES (source.TestDateTime, source.TestData);
GO

--show fragmentation after MERGE
SELECT * 
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(N'dbo.Test'), NULL, NULL, 'DETAILED');
GO

【讨论】:

    猜你喜欢
    • 2015-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多