【问题标题】:SQL - Poor Performance SELECT Query on 377 million tableSQL - 对 3.77 亿张表的性能不佳的 SELECT 查询
【发布时间】:2016-09-13 06:48:33
【问题描述】:

我得到了一个结构如下的表:

Id | clientid | type | timeStamp | message |

我正在使用此查询来获取表的第一行以开始删除行,但正在使数据库崩溃:

SELECT TOP 10 [id]
      ,[clientid]
      ,[type]
      ,[timeStamp]
      ,[message]
FROM [db].[dbo].[table]
WHERE timeStamp LIKE '%2014-01-01 00:00:00.000%'

有没有什么方法可以在到达timeStamp'2016-01-01 00:00:00.000'之前不崩溃地获取第一行并删除它们?

【问题讨论】:

  • WHERE timeStamp LIKE '%2014-01-01 00:00:00.000%'背后的逻辑是什么?
  • 我试图获得第一行,我正在考虑按日期查找,但这不是解决方案。 @马特
  • 您的时间戳是 DATETIME 列吗?此列是否有索引?
  • 你应该在这个列上设置一个索引...
  • 你需要增加sql请求的超时时间。

标签: sql sql-server performance bigdata


【解决方案1】:

简单吗?

WHERE timeStamp = '2014-01-01 00:00:00.000'

【讨论】:

  • 这也使我的数据库崩溃:/
【解决方案2】:

当没有可以添加到日期时间的任何额外内容时,不确定为什么要使用 LIKE

使用=

SELECT TOP 10 [id]
      ,[clientid]
      ,[type]
      ,[timeStamp]
      ,[message]
FROM [db].[dbo].[table]
WHERE timeStamp ='2014-01-01 00:00:00.000'

或者,如果您想从那天获得所有信息,请使用 CONVERT 函数。 (我选择了103,因为我看到你来自西班牙并使用DD/MM/YYYY 格式)。

SELECT TOP 10 [id]
      ,[clientid]
      ,[type]
      ,[timeStamp]
      ,[message]
FROM [db].[dbo].[table]
WHERE CONVERT(VARCHAR(11),timeStamp ,103) = '01/01/2014'

【讨论】:

    【解决方案3】:

    您为什么使用 LIKE ?这无疑会减慢您的查询速度,LIKE 通常用于比较部分字符串。

    您可以只截断日期时间,并使用普通比较:

    SELECT TOP 10 [id]
          ,[clientid]
          ,[type]
          ,[timeStamp]
          ,[message]
    FROM [db].[dbo].[table]
    WHERE cast(timeStamp AS DATE) = '2014-01-01'
    

    【讨论】:

      【解决方案4】:

      我不确定我是否理解正确:

      如果您在 TimeStamp 列上设置索引,那么过滤给定日期更大或更小的行应该绝对快速。

      这些行将从您的表中删除时间戳小于 2016-01-01 的所有内容。仅保留当前条目...

      注意:小心!不要针对真实数据进行测试! :-)

      DELETE FROM myTable
      WHERE ID IN(SELECT ID 
                  FROM myTable AS innerTbl 
                  WHERE innerTbl.Timestamp<{ts'2016-01-01 00:00:00'}
                 )
      

      更新

      这将在每次调用中删除 1000 行。 “GO”后面的数字会执行这个sn -p 377000次。用较小的数字进行测试...

      BEGIN TRANSACTION;
      DELETE FROM myTable
      WHERE ID IN(SELECT TOP 1000
                  ID 
                  FROM myTable AS innerTbl 
                  WHERE innerTbl.Timestamp<{ts'2016-01-01 00:00:00'}
                 );
      COMMIT;
      GO 377000
      

      【讨论】:

      • 我遇到了问题.. 我的光盘中只有 9 GB 的可用空间。所以我不能备份表,我必须开始删除旧行以获得空间。如果我索引 3.77 亿行,它会崩溃吗?
      • @devtreat 索引将需要大约 3 GB,如此繁重的删除操作会炸毁事务日志...我建议创建索引并分段删除(每个事务都已提交) .我会尽快编辑我的答案...
      • 这个查询会减少磁盘空间吗? GO 377000 抛出错误“找不到存储过程”,“'377000' 附近的语法不正确”
      • @devtreat,一旦行被删除(可能在删除行时多次,你应该缩小你的数据库。阅读DBCC SHRINKDATABASE
      • @devtreat 你怎么称呼这个? “GO”甚至不是 SQL 命令,只是 SSMS 中的 end-of-batch 登录。甚至可以改变这一点。您收到的错误让我想,您没有从 SSMS 执行此操作,是吗?
      【解决方案5】:

      我猜 ID 是一个身份列?可能懒惰的 DBA 也将它作为聚集索引保留了?做吧

      DELETE TOP (10) from [Table] order by ID ASC
      

      请注意,尽管在完整日志记录模式下这会很激烈

      【讨论】:

        【解决方案6】:

        你应该在时间戳上创建一个索引。

        您可以设置一个过滤索引,因为它是一个相当大的表。

        CREATE NONCLUSTERED INDEX IX_Table_timestamp ON Table([timestamp]) WHERE CONVERT(DATE,[timestamp]) = '2014-01-01';
        

        然后小批量删除行。

        来自Aaron Bertrand 的旧帖子中的代码。

        SET NOCOUNT ON;
        
        DECLARE @r INT;
        
        SET @r = 1;
        
        WHILE @r > 0
        BEGIN
          BEGIN TRANSACTION;
        
          DELETE TOP (10000) 
            dbo.Table
            WHERE CONVERT(DATE,[timestamp]) = '2014-01-01';
        
          SET @r = @@ROWCOUNT;
        
          COMMIT TRANSACTION;
        
        END
        

        【讨论】:

          【解决方案7】:

          如果您有一个自动递增的列 ID,那么为什么要使用 ID 选择第一行?

          DELETE TOP (1) from [Table] order by ID ASC
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-07-10
            • 1970-01-01
            • 2019-04-17
            • 2020-04-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多