【问题标题】:Optimization suggestions for T-SQL Database and/or queryT-SQL 数据库和/或查询的优化建议
【发布时间】:2016-04-22 21:43:19
【问题描述】:

我正在设法减少执行此查询所需的时间。现在,根据服务器的不同,它需要 30-90 分钟。我仍在学习索引,因此这是未来项目的一个选项,我也在寻找可能有助于优化查询本身的建议。数据库非常大,在主表中包含 10-1 亿行。任何的意见都将会有帮助!

(我已将列名更改为对这篇文章更具描述性)

USE MyDB
DECLARE @interval int = 6 
DECLARE @mindate DATETIME = (SELECT DATEADD(HOUR, DATEDIFF(HOUR,0,MIN(DateColumn))/@interval*@interval, 0) FROM MyTable1)
DECLARE @maxdate DATETIME = (SELECT DATEADD(HOUR, DATEDIFF(HOUR,0,MAX(DateColumn))/@interval*@interval, 0) FROM MyTable1)
DECLARE @end DATETIME     = (SELECT DATEADD(HOUR, DATEDIFF(HOUR,0,GETDATE())/@interval*@interval, 0))

--=========================================================
--   Update all DATEADD statements to desired interval
--=========================================================
;WITH cte
     AS (SELECT 
            [StartTime]  = CONVERT(datetime,@mindate),
            [EndTime]    = DATEADD(HOUR, @interval, CONVERT(datetime,@mindate))
         UNION ALL
         SELECT 
            DATEADD(HOUR, @interval, [StartTime]),
            DATEADD(HOUR, @interval, [EndTime])
         FROM   cte
         WHERE  [EndTime] < @end)
--==========================================================
SELECT 
    [ServerName]     = ServerName,
    [StartTime]      = CONVERT(varchar, [StartTime], 121),
    [EndTime]        = CONVERT(varchar, [EndTime], 121),
    [ItemsMigrated]  = COUNT(ItemIDColumn),
    [SizeMigrated]   = ISNULL(SUM(ItemSizeColumn),0),
    [ItemsFailed]    = (SELECT COUNT(*) FROM MyTable2 WHERE ItemStatusColumn = "Failed")
FROM cte
LEFT JOIN MyTable1 cr ON cr.DateColumn >= [StartTime] AND cr.DateColumn < [EndTime]
GROUP BY cte.StartTime, cte.EndTime
ORDER BY cte.StartTime
OPTION (MAXRECURSION 0)

【问题讨论】:

  • 你检查执行计划了吗?到目前为止,您创建了哪些索引?
  • 尝试将 cte 实现为 #temp
  • 这太疯狂了 "[ItemsFailed] = (SELECT COUNT(*) FROM MyTable2 WHERE ItemStatusColumn = "Failed")" 为什么不只计算一次呢?
  • 您可能需要查看here 以获取有关提高 CTE 性能的提示。您的查询的典型日期范围是什么?
  • 你能给出一份需求清单——你到底想做什么?看起来您正在做很多可以简单完成的事情,但它太迟钝了,我不确定目标是什么。例如——您要四舍五入到最接近的 6 小时标记?然后得到最小值和最大值?

标签: sql-server tsql indexing query-optimization


【解决方案1】:

删除一个遍历整个数据集的 select 语句:

DECLARE @mindate DATETIME = (SELECT DATEADD(HOUR, DATEDIFF(HOUR,0,MIN(DateColumn))/@interval*@interval, 0) FROM MyTable1)
DECLARE @maxdate DATETIME = (SELECT DATEADD(HOUR, DATEDIFF(HOUR,0,MAX(DateColumn))/@interval*@interval, 0) FROM MyTable1)

可以

DECLARE @mindate DATETIME
DECLARE @maxdate DATETIME

SELECT @mindate = DATEADD(HOUR, DATEDIFF(HOUR,0,MIN(DateColumn))/@interval*@interval, 0), 
       @maxdate = DATEADD(HOUR, DATEDIFF(HOUR,0,MAX(DateColumn))/@interval*@interval, 0)
FROM MyTable1

此外,将 datetime 转换为 datetime 将没有任何效果,编译器将忽略这一点,但您将 datetime 转换为每行的 varchar。这将增加 1 亿行。 而是执行一次,因为这些日期时间不会改变。

SELECT 
  [StartTime]  = CONVERT(datetime,@mindate),
  [EndTime]    = DATEADD(HOUR, @interval, CONVERT(datetime,@mindate))
  [StartTimeS]  = CONVERT(varchar, @mindate, 121),
  [EndTimeS]    =  CONVERT(varchar, DATEADD(HOUR, @interval, CONVERT(datetime,@mindate)), 121),
  [ItemsFailed] = (SELECT COUNT(*) FROM MyTable2 WHERE ItemStatusColumn = "Failed")
UNION ALL

SELECT 
  DATEADD(HOUR, @interval, [StartTime]),
  DATEADD(HOUR, @interval, [EndTime])
  CONVERT(varchar, DATEADD(HOUR, @interval, [StartTime]), 121),
  CONVERT(varchar, DATEADD(HOUR, @interval, [EndTime]), 121),
  [ItemsFailed]
FROM   cte

那么你有

SELECT 
  [ServerName]     = ServerName,
  [StartTime]      = StartTimeS,
  [EndTime]        = EndTimeS,
  [ItemsMigrated]  = COUNT(ItemIDColumn),
  [SizeMigrated]   = ISNULL(SUM(ItemSizeColumn),0),
  [ItemsFailed]  

但请记住,100m 行仍然是 100m 行,这需要一些时间。如果您要进行任何联接,则需要一个索引才能避免对性能造成严重影响。

(例如,在没有索引的情况下连接到 10 行表将导致数据库上的额外读取次数达到 10 亿次。)

如果您有 1 亿行,请创建一些索引。

【讨论】:

  • 我实施了您的更改和@Reboon 建议的一些更改,查询从~20 分钟到~8 分钟。大帮助!谢谢各位!
【解决方案2】:

1。 正如其他人已经提到的,您可以执行以下语句并将其存储在变量中或将其作为子查询加入。您当前的操作方式(至少根据我的经验),它将在您返回的每一行中执行一行,这肯定会减慢您的查询速度。

(SELECT COUNT(*) FROM MyTable2 WHERE ItemStatusColumn = "Failed")

2。 那么下面的转换就不需要了,如果你@Mindate 已经是一个日期时间了。

CONVERT(datetime,@mindate)

3。 正如其他人已经提到的,将您的 CTE 加载到临时表或表变量中。重要的是,设置哪些列是 NULL / NOT NULL 以及哪一个是主键(如果可能的话),因为这会加快速度。

4。 有点细节,但@interval 是否需要为int?我很确定您可以使用 tinyint 作为数据类型而不是 int。

5。 根据您的表 MyTable1,您可以使用“with (nolock)”,这有时可以根据您的情况大大提高性能。但是要非常小心,因为“with (nolock)”如果在您尝试访问表期间有很多插入/更新/删除操作,您可能会得到不完整的数据。如果这是您的临时表,您是唯一一个访问该表的人,那么使用“with (nolock)”来加快速度可能是安全的。

https://www.mssqltips.com/sqlservertip/2470/understanding-the-sql-server-nolock-hint/

【讨论】:

  • 1.好点子。添加。 2. 有几个CONVERT 语句分散在查询中。老实说,它们中的大多数都是从我团队的另一位成员那里复制/粘贴的。我目前正在没有他们的情况下进行测试,以确保数据仍然正确。 3.我会查看临时表,看看它会增加什么样的收益。 4.可以去tinyint。无论如何都不应该需要高于 255 的值。我会去做的。 5. 这个查询应该是拉取已经提交的数据,所以这可能有效。但是,它是我们所有迁移写入发生的表。我会调查的。
猜你喜欢
  • 1970-01-01
  • 2022-01-15
  • 1970-01-01
  • 2014-11-04
  • 2011-07-03
  • 2015-01-18
  • 1970-01-01
  • 2012-03-27
相关资源
最近更新 更多