【问题标题】:TSQL - clone records for a dayTSQL - 克隆一天的记录
【发布时间】:2016-04-11 23:18:09
【问题描述】:

我正在创建一个带有大型 MSSQL 数据库的实验室。 为了简化问题,我们假设它仅适用于 1 个表(如果我能找到更好的解决方案,我可以对所有表都这样做)

我的表中有 1 天的数据,比如 525k 到 630k 条记录。我想复制不同日期的数据(创建历史记录)。

我已经尝试了不同的方法,但我发现它很长。最初,该过程需要 91 小时来复制 1 天...我将其缩短到 16 分钟来处理 1 天(这使得复制一年大约需要 91 小时)。我想知道是否有任何工具或东西可以快速复制数据或创建历史记录?

这是我现在拥有的:

    Declare @iDateCnt int=1,
            @TmpDate datetime,
            @iDate int=365, -- counter to create a years worth of history.
            @DateStart datetime = '2015-12-22'
    Select F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, getdate() as F11
    into #TMP_Table1
    From Table1 where F1 = @DateStart -- Template Day to be duplicated

While @iDateCnt<=@iDate
Begin 
    Set @TmpDate = @DateStart-@iDateCnt
    Delete from Table1 where F1 = @TmpDate
    Insert into Table1 (F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11)
        Select @TmpDate as F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, getdate() as F11
        from #Tmp_Table1

    Drop #Tmp_Table1
End 

【问题讨论】:

  • 它这么慢的原因是你在一个循环中一次只做一个记录。 SQL 没有任何机会对其进行优化,它(很可能)会在几秒钟内作为批处理工作。
  • 创建一个日期表(或 CTE),其中包含您要填充的每个日期的一行,然后交叉连接到该表而不是运行循环。
  • 你为什么要为此使用while循环?这就是性能问题的症结所在。这可以重写为基于单个集合的插入。
  • @Tab Alleman 我认为这可能是个好主意。但我不确定我是否理解这将如何工作。我会删除不属于模板日期的任何内容并交叉连接日期表并使用日期表中的日期作为 F1 进行 1 次大插入。正确的?请添加为答案,我会投票作为我正在寻找的答案。

标签: sql-server tsql sql-server-2012 sql-server-2014


【解决方案1】:

要将我的评论表述为答案,您首先要创建一个具有 datetime 列的表,其中每个要使用克隆数据填充的日期对应一行。假设我们称之为tblDates。然后,您只需对现有脚本执行此操作:

Select F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, getdate() as F11
into #TMP_Table1
From Table1 where F1 = @DateStart -- Template Day to be duplicated

    Insert into Table1 (F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11)
        Select tblDates.DtColumn as F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, getdate() as F11
        from #Tmp_Table1
        CROSSJOIN tblDates

    Drop #Tmp_Table1
End 

这将为tblDates 中的每一行创建#TMP_Table1 中所有数据的副本,日期来自F1 列中的tblDates(您之前使用循环变量的位置)。

【讨论】:

  • 根据您的评论这正是我最终所做的。谢谢。
  • 一整年重复记录的结果:磁盘空间不足,一切都挂了。由于我的 1 天测试用时 25 秒,我预计一整年最多需要 3 小时,但由于数据集的大小,我怀疑这会表现得更好。我有一种感觉,我需要一次循环并执行块,但由于磁盘空间更多,我将拭目以待,看看全年插入的表现如何。
  • 您可能会获得更好的性能循环这个解决方案并将其分解成块。创建 600k 行的 365 个副本……这需要一段时间。 :)
  • 我也这么认为。问题是我正在为整个数据库执行此操作,而有些表并没有那么大,不需要分解。我必须想出一种方法来动态决定是否应该按块插入数据。还不知道怎么做。
【解决方案2】:

一次执行一次插入总是很慢,尤其是对于索引量很大的表。

相反,您应该将其作为单个插入来执行,类似于

DECLARE @startDate DATETIME = '2015-12-22'
DECLARE @endDate = DATEADD(days,365, @startDate)

Insert into Table1 (F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11)        
Select F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, getdate() 
FROM Table1 where F1 > @startDate AND F1 <= @endDate

【讨论】:

  • @Tmpdate 是我循环播放的日期。我看不出这里有什么不同。
  • @JohnG - 抱歉,不小心留下了@tmpdata。它是从源插入到目标的简单选择(在您的情况下是同一张表)
  • 但这不起作用,因为我需要将 F1 更改为一年中的每一天。我在表格中的模板有 2015-12-22 日期的 525K 记录;我需要为那一年的每隔一天复制这组数据......所以F1每隔一天取一次值。我认为 Tab Alleman 使用交叉连接的答案是正确的。您在解决方案中所缺少的只是添加一个日期表,交叉加入它并将该日期用作 F1。
  • @JohnG:这听起来有点奇怪。实际的最终结果是什么——即数据的使用?我想不出一个用例将相同的数据复制到一年中的每一天。
  • @HardCode 我们希望在与我们客户所拥有的数据库大小相似的数据库上模拟部署、维护计划和增长分析。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多