sql server 性能_SQL Server性能神话破灭

sql server 性能

介绍 (Introduction)

In this article, we will verify if the following statements are true or they are just myths:

在本文中,我们将验证以下陈述是正确的还是仅仅是神话:

  1. Truncate is faster than a delete and it consumes less space in the logs

    截断比删除快,并且在日志中占用的空间更少
  2. Do not use cursors to Create or drop multiple objects because it is very slow

    不要使用游标创建或删除多个对象,因为它非常慢
  3. Do not use cursors to convert or process row values because it is very slow

    不要使用游标转换或处理行值,因为它非常慢

入门 (Getting started)

  1. Truncate is faster than a delete and it consumes less space in the logs 截断比删除快,并且在日志中占用的空间更少

    To delete all rows in a table, the best option is to use a truncate statement. This option is faster and consumes fewer system and log resources. But is it true?

    要删除表中的所有行,最好的选择是使用截断语句。 此选项更快,并且消耗更少的系统和日志资源。 但这是真的吗?

    We will test if that is true by creating a table with a million rows in two different databases. We will test the delete statement in one and in another; we will test the truncate statement.

    我们将通过在两个不同的数据库中创建具有一百万行的表来测试这是否成立。 我们将在另一个测试删除语句; 我们将测试截断语句。

    We will use the simple recovery model in the database to reduce the number of entries in the log.

    我们将使用数据库中的简单恢复模型来减少日志中的条目数。

    We will first create 2 databases:

    我们将首先创建2个数据库:

    create database deleteDemo

    创建数据库deleteDemo

    create database truncateDemo

    创建数据库truncateDemo

    These databases will be used to compare the results of the delete and truncate statements:

    这些数据库将用于比较delete和truncate语句的结果:

    Figure 0. 2 databases created 图0. 2个数据库已创建

    The initial size is 16 MB for both databases (this value depends on the SQL Server version):

    两个数据库的初始大小均为16 MB(此值取决于SQL Server版本):

    Figure 1. Database size 图1.数据库大小

    We will set the recovery model to simple. This mode reduces the entries in the log. However, you cannot restore the database in a specific time with this option.

    我们将恢复模型设置为简单。 此模式将减少日志中的条目。 但是,您不能使用此选项在特定时间内还原数据库。

    In the Database Properties, go to Options and in Recovery model, select simple:

    在数据库属性中,转到选项,然后在恢复模型中选择简单:

    Figure 2. Recovery model 图2.恢复模型

    You can also change the recovery model using T-SQL:

    您还可以使用T-SQL更改恢复模型:

     
    USE [master]
    GO
    ALTER DATABASE [deleteDemo] SET RECOVERY SIMPLE WITH NO_WAIT
    GO
     
    USE [master]
    GO
    ALTER DATABASE [truncateDemo] SET RECOVERY SIMPLE WITH NO_WAIT
    GO
     
    

    In order to test the truncate and delete statements, we will create a table named myprices in each database with a million rows:

    为了测试截断和删除语句,我们将在每个数据库中创建一个名为myprices的表,该表具有一百万行:

     
    --Table truncateDemo
    Use truncateDemo
    GO
       with listprices
        as(
           select 1 id, CAST(RAND(CHECKSUM(NEWID()))*1000000 as int) prices
            union  all
            select id + 1, CAST(RAND(CHECKSUM(NEWID()))*1000000 as int)  prices
            from listprices
            where 
              id <= 1000000
          )
     
     
     
        select *
    	into myprices
        from listprices
        OPTION(MAXRECURSION 0)
     
    --Table deleteDemo
    Use deleteDemo
    GO
       with listprices
        as(
           select 1 id, CAST(RAND(CHECKSUM(NEWID()))*1000000 as int) prices
            union  all
            select id + 1, CAST(RAND(CHECKSUM(NEWID()))*1000000 as int)  prices
            from listprices
            where 
              id <= 1000000
          )
     
     
     
        select *
    	into myprices
        from listprices
        OPTION(MAXRECURSION 0)
     
    

    Note that the size of the database has increased from 16 MB to 144. This is because of the large number of rows inserted. The log file grows even with a Recovery Model set to simple:

    请注意,数据库的大小已从16 MB增加到144。这是因为插入了很多行。 即使将恢复模型设置为简单,日志文件也会增长:

    Figure 3. Database Size after inserting a million rows 图3.插入一百万行后的数据库大小

    We will first test the delete statement:

    我们将首先测试delete语句:

     
    USE deleteDemo
    GO
    DELETE FROM [dbo].[myprices]
     
    

    As you can see, it takes 23 seconds to delete a million rows:

    如您所见,删除一百万行需要23秒:

    Figure 4. Time required to delete a million rows 图4.删除一百万行所需的时间

    If we check the database properties, we will notice that the database size increased to 528 MB!

    如果我们检查数据库属性,我们将注意到数据库大小增加到528 MB!

    Figure 5. Database growth after deleting a million rows 图5.删除一百万行后数据库的增长

    Now, we will try the truncate table statement:

    现在,我们将尝试截断表语句:

     
    USE truncateDemo
    GO
    TRUNCATE TABLE [dbo].[myprices]
     
    

    When you run truncate, the execution time is 0 seconds:

    当您运行truncate时,执行时间为0秒:

    Figure 6. Truncate execution time 图6.截断执行时间

    If we check the log file, we notice that the database size did not change!

    如果我们检查日志文件,则会发现数据库大小没有改变!

    Figure 7. Database size after truncating 图7.截断后的数据库大小

    As you can see, you save space and the execution time is better with truncate. If you need to delete all the rows, it is a must to use it.

    如您所见,您可以节省空间,并且使用truncate可以缩短执行时间。 如果需要删除所有行,则必须使用它。

  2. Do not use cursors Create or drop multiple objects because they are very slow 不要使用游标创建或删除多个对象,因为它们非常慢

    In the next example, we will show 2 ways to delete multiple SQL Server Objects. One using cursors and another using CTEs to generate statements. We will see which option is faster.

    在下一个示例中,我们将展示两种删除多个SQL Server对象的方法。 一个使用游标,另一个使用CTE生成语句。 我们将看到哪个选项更快。

    First, we will create 10000 tables using T-SQL:

    首先,我们将使用T-SQL创建10000个表:

     
    with ctesequence
        as(
           select 1 id
            union  all
            select id + 1
            from ctesequence
            where 
              id < 10000
          )
     
        select 'create table table'+cast(id as varchar(8))+'(id int)'
        from ctesequence
        OPTION(MAXRECURSION 0)	
     
    

    This code will create the following T-SQL statements:

    此代码将创建以下T-SQL语句:

     
    Create table table1
    Create table table2
    Create table table3
    ….
    Create table table10000
     
    

    Copy and paste the 10,000 create table statementsstatements and execute them.

    复制并粘贴10,000个create table语句并执行它们。

    You will have 10,000 tables created.

    您将创建10,000个表。

    We will first create a cursor named delete_tables that will drop all the tables that start with the name table:

    我们将首先创建一个名为delete_tables的游标,该游标将删除所有以名称表开头的表:

     
    --create a cursor named delete_tables
    DECLARE delete_tables CURSOR FAST_FORWARD FOR
    --The query will show all the tables whose name starts with the word table
    SELECT [TABLE_NAME]
    FROM [INFORMATION_SCHEMA].[TABLES] 
    WHERE [TABLE_NAME] like 'table%'
    OPEN delete_tables
    DECLARE @tablename varchar(30)
    FETCH NEXT FROM delete_tables into @tablename
    WHILE @@FETCH_STATUS <> -1
    BEGIN
    --we are dropping all the tables
    execute ('drop table '+@tablename )
     
    FETCH NEXT FROM delete_tables into @tablename
     
    END
    CLOSE delete_tables
    DEALLOCATE delete_tables
    GO
     
    

    The cursor will delete the 10,000 rows in 1 minute, 30 seconds:

    光标将在1分钟30秒内删除10,000行:

    Figure 8. Execution time 图8.执行时间

    We will try now another method to delete 1,000 tables. Create the 1,000 tables again and run these statements:

    现在,我们将尝试另一种删除1,000个表的方法。 再次创建1,000个表并运行以下语句:

     
    	 with ctesequence
        as(
           select 1 id
            union  all
            select id + 1
            from ctesequence
            where 
              id < 100000
          )
     
        select 'drop table table'+cast(id as varchar(8))
        from ctesequence
        OPTION(MAXRECURSION 0)
     
    

    This sentence will generate 10,000 statements to drop 10,000 tables. Copy and paste them and finally execute them:

    这句话将生成10,000条语句以删除10,000个表。 复制并粘贴它们,最后执行它们:

    Figure 9. 10000 drop statements took 1 minute 15 seconds 图9. 10000个drop语句花费了1分15秒

    In this example, we are running drop statements sequentially in both cases. Running without cursors is a little bit faster because of the locks and the where clause used in the cursors. However, it is not a big difference in this case. The advantage of using cursors is that you delete them at once and you do not need to copy and paste the drop statements.

    在此示例中,两种情况下我们都按顺序运行drop语句。 由于游标中使用了锁和where子句,因此没有游标的运行要快一些。 但是,在这种情况下,差别不大。 使用游标的优点是您可以一次删除它们,而无需复制和粘贴drop语句。

  3. Do not use cursors to convert or process row values because they are very slow 不要使用游标转换或处理行值,因为它们很慢

    In the last example, we will convert rows to a column separated by commas.

    在最后一个示例中,我们将行转换为以逗号分隔的列。

    For example, if we have a table like this:

    例如,如果我们有一个这样的表:

    ID 1 2 3

    ID 1 2 3

    We want to have the following result:

    我们希望得到以下结果:

    1,2,3

    1,2,3

    We will create a table with 1000 values named #mysequence:

    我们将创建一个包含1000个值的表,名为#mysequence:

     
    with ctesequence
        as(
           select 1 id
            union  all
            select id + 1
            from ctesequence
            where 
              id < 1000
          )
     
     select * 
     into #mysequence
     from ctesequence
      OPTION(MAXRECURSION 0)
     
    

    We will first try with cursors:

    我们将首先尝试使用游标:

     
    --Create a cursor
    DECLARE concatenate_values CURSOR FOR
    SELECT cast(id as varchar(30))
    FROM #mysequence 
    OPEN concatenate_values
    DECLARE @value varchar(max)=''
    DECLARE @initialvalue varchar(8)=''
    FETCH NEXT FROM concatenate_values into @initialvalue
    WHILE @@FETCH_STATUS <> -1
    BEGIN
    --Concatenate the values
    SELECT @value= @value+','+@initialvalue
     
    FETCH NEXT FROM concatenate_values into @initialvalue
     
    END
    CLOSE concatenate_values
    DEALLOCATE concatenate_values
    select @value
     
    

    The cursor will display the values separated by commas in 3 seconds:

    光标将在3秒内显示用逗号分隔的值:

    Figure 10. Cursor execution time 图10.游标执行时间

    We will now try to use something different from cursors. To verify the time we will set statistics and time to on in order to have the execution time in milliseconds (ms):

    现在,我们将尝试使用与游标不同的东西。 为了验证时间,我们将统计信息和时间设置为开,以使执行时间以毫秒(ms)为单位:

     
    SET STATISTICS io ON
    SET STATISTICS time ON
    GO
     
    

    We will first concatenate the values using a variable named @concatenatevalues and concatenate the values separated by commas:

    我们将首先使用名为@concatenatevalues的变量将这些值连接起来,并用逗号分隔这些值:

     
    declare @concatenatedvalues varchar(max)
    SET @concatenatedvalues = ''
    select @concatenatedvalues = @concatenatedvalues + cast(id as 
    varchar(30))+ ',' from #mysequence
    select SUBSTRING(@concatenatedvalues, 0, LEN(@concatenatedvalues)) as 
    [values]
     
    

    These queries will take less than 100 ms in average:

    这些查询平均需要不到100毫秒的时间:

    Figure 11. Execution time 图11.执行时间

    Another option is to use the XML PATH, this option is shorter, easier to read, but it takes longer time:

    另一个选项是使用XML PATH,该选项更短,更易于阅读,但需要更长的时间:

     
    SELECT ',' + cast(id as varchar(30))
    FROM #mysequence
    FOR XML PATH('') 
     
    

    Figure 12. XML PATH solution 图12. XML PATH解决方案

结论 (Conclusions)

In this article, we verified that truncate is more efficient that a delete statement. In our example, we verified that truncating 1 million rows does not fill the log file and it takes less than 1 second. By the other hand, we found that the delete statement increased the database size from 144 Mb to 528 Mb. In addition, the execution time to delete was 23 seconds.

在本文中,我们验证了截断比删除语句更有效。 在我们的示例中,我们验证了截断一百万行不会填充日志文件,并且花费了不到1秒的时间。 另一方面,我们发现delete语句将数据库大小从144 Mb增加到528 Mb。 另外,删除的执行时间为23秒。

第一次测试 (First test)

Statement Time (seconds) Growth Size in Mb
Truncate 0 0
Delete 23 384
声明 时间(秒) 增长量(兆)
截短 0 0
删除 23 384

Conclusion: When you need to delete all the rows, you should use Truncate.

结论:当需要删除所有行时,应使用Truncate。

第二次测试 (Second test)

In the second test, we dropped 10000 tables using cursors and then using CTE to generate the statements.

在第二个测试中,我们使用游标删除了10000个表,然后使用CTE生成了语句。

Cursors took 1 minute 30 seconds and CTE took 1 minute with 15 seconds.

游标花了1分钟30秒,CTE花了1分钟15秒。

Method Time (seconds)
Cursor 90
CTE 75
方法 时间(秒)
光标 90
CTE 75

The advantage of Cursors is that you can execute and delete the tables at once. In the CTE method, you need to generate the statements and after that, copy and paste the statements. The CTE code is simpler.

游标的优点是您可以一次执行和删除表。 在CTE方法中,您需要生成语句,然后复制并粘贴语句。 CTE代码更简单。

Conclusion: If you need to delete, create 100+ objects, using cursors is not a bad choice. Do not use Cursors if you can work with SETs of data.

结论:如果需要删除,使用光标创建100个以上的对象不是一个坏选择。 如果可以使用SET数据集,请不要使用Cursors。

第三次测试 (Third test)

In our third test, we converted 1000 rows into a single variable of 1000 values separated by commas.

在我们的第三个测试中,我们将1000行转换为1000个值的单个变量,并用逗号分隔。

We used 3 methods:

我们使用了3种方法:

  • Cursors

    游标
  • Simple T-SQL

    简单的T-SQL
  • T-SQL with XML PATH mode

    具有XML PATH模式的T-SQL
Method Time (seconds)
Cursor 3
Simple T-SQL 0.06
T-SQL with XML PATH 0.3
方法 时间(秒)
光标 3
简单的T-SQL 0.06
具有XML PATH的T-SQL 0.3

Conclusion: When you need to update rows in a table, delete rows, insert or search values, using cursors isn’t a good idea. As you can see in this example, it is extremely slow.

结论:当您需要更新表中的行,删除行,插入或搜索值时,使用游标不是一个好主意。 如您在本例中所见,它非常慢。

XML PATH is commonly used in many scenarios to manipulate and modify row values to replace the cursors. However, there are sometimes faster solutions.

XML PATH通常在许多情况下用于操纵和修改行值以替换游标。 但是,有时会有更快的解决方案。

翻译自: https://www.sqlshack.com/sql-server-performance-myth-busters/

sql server 性能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值