Sql server 合并语句与视图

Sql server 合并语句与视图,sql-server,performance,merge,view,Sql Server,Performance,Merge,View,我有一个合并语句,如下所示 MERGE INTO test a USING @temptable b ON a.[col1] = b.[col1] WHEN MATCHED THEN UPDATE SET a.[col2] = b.[col2], a.[col3] = b.[col3], a.[col4] = b.[col4], a.[col5] = b.[co

我有一个合并语句,如下所示

  MERGE INTO test a
  USING @temptable b ON a.[col1] = b.[col1] 

  WHEN MATCHED THEN
      UPDATE 
          SET a.[col2] = b.[col2],
              a.[col3] = b.[col3],
              a.[col4] = b.[col4], 
              a.[col5] = b.[col5]

  WHEN NOT MATCHED THEN
      INSERT VALUES(b.[col1], b.[col2], b.[col3], b.[col4], b.[col5]);
表和表类型结构如下(
col1
已非聚集索引)

我想解决的问题是,当目标表变大时,完成合并需要更多的时间。在我的场景中,一个批次将有90%的插入和10%的更新。因此,我尝试通过使用类似于
[col1]>500000的条件来使用视图进行合并

根据我的测试,速度从35092ms提高到23336ms,将相同的50000条记录向总记录为8084328行的表中插入50次


这是处理大型表合并的正确方法吗?

数据设置

我用一个糟糕的数据设置做了一些测试,看看SQL Server做了什么。不同设置的主要区别在于

  • 对数据进行排序
  • 最小化读取
我发现重要的是

  • 使用表变量(例如,
    @tentable
    )通常会导致较差的内存授予,因为它只需要一行。然后它溢出到磁盘,导致磁盘速度变慢。临时表
    #tentable
    通常工作得更好
  • 对于原始表
    test
    而言,当
    col1
    上只有一个非聚集索引时,效果最好。本质上,为了合并的目的,我们只关心行是否存在,现在col2到col5是什么
我发现对速度没有帮助

  • 测试添加主键(或聚集索引)
    ,而良好实践imo会降低速度,因为插入需要更新相关聚集索引。如果要插入到聚集索引的中间,则需要比添加到末尾花费更多的时间
  • 对于保存数据的临时表
    #tentable
    ,它通常在没有任何特定的其他索引的情况下工作得相对较好。在某些情况下,添加聚集索引,例如,
    创建聚集索引#CX#tentable ON#tentable(col1、col2、col3、col4、col5)
    稍微提高了速度,在其他情况下会导致更多的读取
一般方法

部分问题是,你担心什么?如果整个过程需要一秒钟的时间来运行(但在这一秒钟内将所有其他内容都锁定),那么这是不是很糟糕?如果保持锁尽可能短是主要的目标,你可以考虑替代的方法。< /P> 其他选择包括

  • 分批进行,例如,一次合并10000个案例。这将最小化“锁定”周期,但会按顺序多次执行。如果您所在的服务器内存可用性较差,并且合并需要的内存比您现有的多,那么这也很有用。然而,它最终会使用更多的总处理能力,如果有人在中间使用它,他们可能只得到部分更新的数据。
  • 将其与分别执行
    更新
    插入
    命令进行比较。其中的每一个都可能比合并要短,并且使用更少的内存,并且总的处理时间可能要长一点。。。但是,根据您可用的资源,它实际上可能会更快。如果这总体上更快,您还可以将这两个数据打包到一个事务中,以阻止任何用户看到部分更新的数据

作为一般规则,我建议避免使用临时表变量,例如
@tentable
。相反,请使用完整的临时表。通常使用表变量的方式没有统计信息,因此SQL Server假定其中只有一行,并为此设计一个查询。此外,为了提供帮助,表中是否有主键,例如,Col1是否可以是主键?如果是这样,您可以将Col1作为原始表和临时表中的主键,这将大大提高速度,因为它们都将在同一字段上预先排序。或者,如果您可以将集群键设置为所有字段的顺序(即使没有主键),我认为这会有所帮助。感谢回复,我们将查看临时表。另一种可能的方法是“预计算”数据,例如,插入哪些行,更新哪些行。这可以减少锁定时间。但是,请注意,如果同时多次运行同一命令,任何多语句结果都可能遇到并发问题。这里主要关注的还是速度,因为这是在我的测试环境中进行的测试。真正的场景是在一个64gb ram的服务器中,10000行数据合并到10亿个记录表需要3到4分钟。该表已按日期分区。数据结构与示例类似,只提供了更多列和包含日期数据类型。id列已被非聚集索引。我还测试了更新和插入,但似乎比合并慢。合并可以是多线程的吗?感谢您提出的建议和您所做的测试。这对我帮助很大。谢谢你的反馈。就大小/分区而言,这超出了我的经验范围。然而,这里有一个建议。对于80%是新的10000行合并,基本上应该是一个3步过程。1) 对于10000行,确定哪些需要插入,哪些是更新。2) 对于已经存在的~2000,更新它们,然后3)对于~8000新的,插入它们。在一个普通的数据库中,这些不应该是坏的。我建议手动尝试将这三个步骤作为单独的命令来确定延迟的位置(例如,步骤1是否需要时间,因为它不使用索引?)
CREATE TYPE [dbo].[test] AS TABLE
       (
            [col1] [int] NULL,
            [col2] [varchar](100) NULL,
            [col3] [varchar](100) NULL,
            [col4] [varchar](100) NULL,
            [col5] [varchar](100) NULL
       )