Raz*_*anu 5 t-sql sql-server-2016
下面的脚本将查询 Ola 的维护解决方案结果表 (CommandLog),并在过去 4 周内对每两周一次的重新索引执行持续时间进行比较。
现在看起来,这是一个快速的技巧,可以帮助我调整维护窗口。但我想删除日期硬编码,这样我就不必在未来的每个周末手动添加一个新的 JOIN。
请提供完整的重写(动态 SQL?)或仅提供有关如何实现此功能或要包含的其他一些有用功能的一些提示。确定更改 sproc 并将额外的输出添加到表中。使用 SQL 2016。如果已经有一个脚本可以满足这个目的,那么很高兴使用那个脚本。
WITH t0 AS
(
SELECT ObjectName, IndexName, IndexType
FROM Tools.dbo.CommandLog
WHERE 1=1
AND DatabaseName = 'testdb'
AND CommandType = 'ALTER_INDEX'
GROUP BY ObjectName, IndexName, IndexType
)
SELECT
t0.ObjectName
,t0.IndexName
,t0.IndexType
,DATEDIFF(ss,t1.StartTime,t1.EndTime) as '20-40 01-06'
,DATEDIFF(ss,t2.StartTime,t2.EndTime) as '5-40 01-07'
,DATEDIFF(ss,t3.StartTime,t3.EndTime) as '20-40 01-13'
,DATEDIFF(ss,t4.StartTime,t4.EndTime) as '5-40 01-14'
,DATEDIFF(ss,t5.StartTime,t5.EndTime) as '20-40 01-20'
,DATEDIFF(ss,t6.StartTime,t6.EndTime) as '5-40 01-21'
,DATEDIFF(ss,t7.StartTime,t7.EndTime) as '20-40 01-27'
,DATEDIFF(ss,t8.StartTime,t8.EndTime) as '5-40 01-28'
FROM t0
LEFT JOIN Tools.dbo.CommandLog as t1 ON t0.ObjectName = t1.ObjectName AND t0.IndexName = t1.IndexName AND t1.StartTime BETWEEN '2018-01-06 00:30:00' AND '2018-01-06 23:59:59'
LEFT JOIN Tools.dbo.CommandLog as t2 ON t0.ObjectName = t2.ObjectName AND t0.IndexName = t2.IndexName AND t2.StartTime BETWEEN '2018-01-07 00:30:00' AND '2018-01-07 23:59:59'
LEFT JOIN Tools.dbo.CommandLog as t3 ON t0.ObjectName = t3.ObjectName AND t0.IndexName = t3.IndexName AND t3.StartTime BETWEEN '2018-01-13 00:30:00' AND '2018-01-13 23:59:59'
LEFT JOIN Tools.dbo.CommandLog as t4 ON t0.ObjectName = t4.ObjectName AND t0.IndexName = t4.IndexName AND t4.StartTime BETWEEN '2018-01-14 00:30:00' AND '2018-01-14 23:59:59'
LEFT JOIN Tools.dbo.CommandLog as t5 ON t0.ObjectName = t5.ObjectName AND t0.IndexName = t5.IndexName AND t5.StartTime BETWEEN '2018-01-20 00:30:00' AND '2018-01-20 23:59:59'
LEFT JOIN Tools.dbo.CommandLog as t6 ON t0.ObjectName = t6.ObjectName AND t0.IndexName = t6.IndexName AND t6.StartTime BETWEEN '2018-01-21 00:30:00' AND '2018-01-21 23:59:59'
LEFT JOIN Tools.dbo.CommandLog as t7 ON t0.ObjectName = t7.ObjectName AND t0.IndexName = t7.IndexName AND t7.StartTime BETWEEN '2018-01-27 00:30:00' AND '2018-01-27 23:59:59'
LEFT JOIN Tools.dbo.CommandLog as t8 ON t0.ObjectName = t8.ObjectName AND t0.IndexName = t8.IndexName AND t8.StartTime BETWEEN '2018-01-28 00:30:00' AND '2018-01-28 23:59:59'
WHERE 1=1
ORDER BY
t0.ObjectName
,t0.IndexName
,t0.IndexType
Run Code Online (Sandbox Code Playgroud)
日历表
正如LowlyDBA和Anti-weakpasswords在他们的评论中指出的那样,您可以找到不止一种生成日历表的方法:
为了回答这个问题,我将生成一个(基本)日历表,只是为了向您展示如何将它与您的数据一起使用,您将在网上找到使用更详细字段(秒、一年中的一天、季度、等等)
Run Code Online (Sandbox Code Playgroud)CREATE TABLE Calendar(cDate datetime, cDay int, cDayOfWeek int, cDayName varchar(20)); DECLARE @date date = '20180101'; WHILE @date <= '20180131' BEGIN INSERT INTO Calendar VALUES (@date, DAY(@date), DATEPART(weekday, @date), DATENAME(weekday, @date)); SET @date = DATEADD(day, 1, @date); END
因此,根据您的查询,您似乎只对获取周六和周日的结果感兴趣。在这种情况下,您应该Calendar
通过day name
或过滤表day of week
。如果您想按 过滤,请查看SET DATEFIRSTday of week
。
Run Code Online (Sandbox Code Playgroud)SELECT * FROM Calendar WHERE cDayName IN ('Saturday', 'Sunday'); GO
日期 | cDay | cDayOfWeek | 日期名称 :------------------ | ---: | ---------: | :------- 06/01/2018 00:00:00 | 6 | 7 | 周六 07/01/2018 00:00:00 | 7 | 1 | 星期日 13/01/2018 00:00:00 | 13 | 7 | 周六 14/01/2018 00:00:00 | 14 | 1 | 星期日 20/01/2018 00:00:00 | 20 | 7 | 周六 21/01/2018 00:00:00 | 21 | 1 | 星期日 27/01/2018 00:00:00 | 27 | 7 | 周六 28/01/2018 00:00:00 | 28 | 1 | 星期日
用随机数据填充 CommandLog 表
现在让我模拟一个 CommandLog 表并用 Jan-01 和 Jan-31 之间的 1000 个随机日期填充它。
Run Code Online (Sandbox Code Playgroud)CREATE TABLE CommandLog ( ObjectName varchar(10), IndexName varchar(10), IndexType int, StartTime datetime, EndTime datetime ); DECLARE @step int = 1; DECLARE @startDate datetime; WHILE @step <= 1000 BEGIN SET @startDate = DATEADD(minute, RAND() * 59, DATEADD(hour, RAND() * 23, DATEADD(day, RAND() * 31, '20180101'))) INSERT INTO CommandLog VALUES('OBJ1', 'INDEX1', 1, @startDate, DATEADD(second, RAND() * 59,@startDate)); SET @step += 1; END /* just to check first 10 records */ SELECT TOP 10 * FROM CommandLog; GO
对象名称 | 索引名称 | 索引类型 | 开始时间 | 时间结束 :--------- | :-------- | --------: | :------------------ | :------------------ OBJ1 | 索引1 | 1 | 16/01/2018 04:36:00 | 16/01/2018 04:36:43 OBJ1 | 索引1 | 1 | 06/01/2018 09:11:00 | 06/01/2018 09:11:33 OBJ1 | 索引1 | 1 | 05/01/2018 14:23:00 | 05/01/2018 14:23:43 OBJ1 | 索引1 | 1 | 10/01/2018 19:53:00 | 10/01/2018 19:53:11 OBJ1 | 索引1 | 1 | 14/01/2018 14:31:00 | 14/01/2018 14:31:53 OBJ1 | 索引1 | 1 | 06/01/2018 18:43:00 | 06/01/2018 18:43:07 OBJ1 | 索引1 | 1 | 21/01/2018 21:52:00 | 21/01/2018 21:52:41 OBJ1 | 索引1 | 1 | 28/01/2018 06:51:00 | 28/01/2018 06:51:03 OBJ1 | 索引1 | 1 | 19/01/2018 08:39:00 | 19/01/2018 08:39:58 OBJ1 | 索引1 | 1 | 30/01/2018 19:57:00 | 30/01/2018 19:57:50
格式化数据
好的,到目前为止一切顺利,在这种情况下我选择了 PIVOT 解决方案,但首先我需要格式化源数据以使其成为可能。可以使用Calendar
表格过滤或分组记录。
ON cl.StartTime >= DATEADD(minute, 30, c.cDate)
AND cl.StartTime < DATEADD(day, 1, c.cDate)
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)/* check first 10 records again */ SELECT TOP 10 cl.ObjectName, cl.IndexName, cl.IndexType, cDayName, CASE cDayName WHEN 'Saturday' THEN QUOTENAME('20-40 ' + FORMAT(cDate, 'dd-MM')) WHEN 'Sunday' THEN QUOTENAME('5-40 ' + FORMAT(cDate, 'dd-MM')) END FormatDate, DATEDIFF(ss, cl.StartTime, cl.EndTime) DiffSeconds FROM CommandLog cl INNER JOIN Calendar c ON cl.StartTime >= DATEADD(minute, 30, c.cDate) AND cl.StartTime < DATEADD(day, 1, c.cDate) WHERE cDayName IN ('Saturday', 'Sunday') GO
对象名称 | 索引名称 | 索引类型 | cDayName | 格式日期 | 差异秒 :--------- | :-------- | --------: | :------- | :------------ | ----------: OBJ1 | 索引1 | 1 | 星期六 | [20-40 06-01] | 33 OBJ1 | 索引1 | 1 | 星期六 | [20-40 06-01] | 7 OBJ1 | 索引1 | 1 | 星期六 | [20-40 06-01] | 25 OBJ1 | 索引1 | 1 | 星期六 | [20-40 06-01] | 40 OBJ1 | 索引1 | 1 | 星期六 | [20-40 06-01] | 0 OBJ1 | 索引1 | 1 | 星期六 | [20-40 06-01] | 33 OBJ1 | 索引1 | 1 | 星期六 | [20-40 06-01] | 13 OBJ1 | 索引1 | 1 | 星期六 | [20-40 06-01] | 52 OBJ1 | 索引1 | 1 | 星期六 | [20-40 06-01] | 50 OBJ1 | 索引1 | 1 | 星期六 | [20-40 06-01] | 15
PIVOT 查询
嗯,这是我试图模拟的 PIVOT 查询。请记住,PIVOT 查询需要众所周知的列数,并且您通常必须动态生成它。
Run Code Online (Sandbox Code Playgroud)SELECT ObjectName, IndexName, IndexType, [20-40 06-01],[5-40 07-01],[20-40 13-01],[5-40 14-01], [20-40 20-01],[5-40 21-01],[20-40 27-01],[5-40 28-01] FROM ( SELECT cl.ObjectName, cl.IndexName, cl.IndexType, CASE cDayName WHEN 'Saturday' THEN '20-40 ' + FORMAT(cDate, 'dd-MM') WHEN 'Sunday' THEN '5-40 ' + FORMAT(cDate, 'dd-MM') END FormatDate, DATEDIFF(ss, cl.StartTime, cl.EndTime) DiffSeconds FROM CommandLog cl INNER JOIN Calendar c ON cl.StartTime >= DATEADD(minute, 30, c.cDate) AND cl.StartTime < DATEADD(day, 1, c.cDate) WHERE cDayName IN ('Saturday', 'Sunday') )src PIVOT ( SUM(DiffSeconds) FOR FormatDate IN ([20-40 06-01],[5-40 07-01],[20-40 13-01], [5-40 14-01],[20-40 20-01],[5-40 21-01], [20-40 27-01],[5-40 28-01]) ) pvt; GO
对象名称 | 索引名称 | 索引类型 | 20-40 06-01 | 5-40 07-01 | 20-40 13-01 | 5-40 14-01 | 20-40 20-01 | 5-40 21-01 | 20-40 27-01 | 5-40 28-01 :--------- | :-------- | --------: | ----------: | ---------: | ----------: | ---------: | ----------: | ---------: | ----------: | ---------: OBJ1 | 索引1 | 1 | 第1196章 1480 | 第747话 1010 | 第1197章 第1031章 901 | 605
生成动态查询
您可以使用STUFF函数动态生成列名。然后简单地连接字符串。
Run Code Online (Sandbox Code Playgroud)DECLARE @cols nvarchar(max); SET @cols = STUFF((SELECT ',' + CASE cDayName WHEN 'Saturday' THEN QUOTENAME('20-40 ' + FORMAT(cDate, 'dd-MM')) WHEN 'Sunday' THEN QUOTENAME('5-40 ' + FORMAT(cDate, 'dd-MM')) END FROM Calendar WHERE cDayName IN ('Saturday', 'Sunday') ORDER BY cDate FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 1, ''); DECLARE @cmd nvarchar(max); SET @cmd = 'SELECT ObjectName, IndexName, IndexType, ' + @cols + ' FROM ( SELECT cl.ObjectName, cl.IndexName, cl.IndexType, CASE cDayName WHEN ''Saturday'' THEN ''20-40 '' + FORMAT(cDate, ''dd-MM'') WHEN ''Sunday'' THEN ''5-40 '' + FORMAT(cDate, ''dd-MM'') END FormatDate, DATEDIFF(ss, cl.StartTime, cl.EndTime) DiffSeconds FROM CommandLog cl INNER JOIN Calendar c ON cl.StartTime >= DATEADD(minute, 30, c.cDate) AND cl.StartTime < DATEADD(day, 1, c.cDate) WHERE cDayName IN (''Saturday'', ''Sunday'') )src PIVOT ( SUM(DiffSeconds) FOR FormatDate IN (' + @cols + ')' + ') pvt'; EXEC sp_executesql @cmd; GO
最后结果
对象名称 | 索引名称 | 索引类型 | 20-40 06-01 | 5-40 07-01 | 20-40 13-01 | 5-40 14-01 | 20-40 20-01 | 5-40 21-01 | 20-40 27-01 | 5-40 28-01 :--------- | :-------- | --------: | ----------: | ---------: | ----------: | ---------: | ----------: | ---------: | ----------: | ---------: OBJ1 | 索引1 | 1 | 第1196章 1480 | 第747话 1010 | 第1197章 第1031章 901 | 605
GROUP BY 解决方案
好的,现在是星期六下午,我除了读一些有趣的书之外无事可做。看看你的查询,我认为你可以用单个 GROUP BY + SUM(CASE... 查询使用Calendar
表替换所有这些表连接。
类似于这个:
Run Code Online (Sandbox Code Playgroud)SELECT cl.ObjectName, cl.IndexName, cl.IndexType, SUM(CASE WHEN cDay = 6 THEN DATEDIFF(ss, cl.StartTime, cl.EndTime) ELSE 0 END) AS [20-40 06-01], SUM(CASE WHEN cDay = 7 THEN DATEDIFF(ss, cl.StartTime, cl.EndTime) ELSE 0 END) AS [5-40 07-01], SUM(CASE WHEN cDay = 13 THEN DATEDIFF(ss, cl.StartTime, cl.EndTime) ELSE 0 END) AS [20-40 13-01], SUM(CASE WHEN cDay = 14 THEN DATEDIFF(ss, cl.StartTime, cl.EndTime) ELSE 0 END) AS [5-40 14-01], SUM(CASE WHEN cDay = 20 THEN DATEDIFF(ss, cl.StartTime, cl.EndTime) ELSE 0 END) AS [20-40 20-01], SUM(CASE WHEN cDay = 21 THEN DATEDIFF(ss, cl.StartTime, cl.EndTime) ELSE 0 END) AS [5-40 21-01], SUM(CASE WHEN cDay = 27 THEN DATEDIFF(ss, cl.StartTime, cl.EndTime) ELSE 0 END) AS [20-40 27-01], SUM(CASE WHEN cDay = 28 THEN DATEDIFF(ss, cl.StartTime, cl.EndTime) ELSE 0 END) AS [5-40 28-01] FROM CommandLog cl INNER JOIN Calendar c ON cl.StartTime >= DATEADD(minute, 30, c.cDate) AND cl.StartTime < DATEADD(day, 1, c.cDate) WHERE cDayName IN ('Saturday', 'Sunday') GROUP BY cl.ObjectName, cl.IndexName, cl.IndexType; GO
对象名称 | 索引名称 | 索引类型 | 20-40 06-01 | 5-40 07-01 | 20-40 13-01 | 5-40 14-01 | 20-40 20-01 | 5-40 21-01 | 20-40 27-01 | 5-40 28-01 :--------- | :-------- | --------: | ----------: | ---------: | ----------: | ---------: | ----------: | ---------: | ----------: | ---------: OBJ1 | 索引1 | 1 | 第1196章 1480 | 第747话 1010 | 第1197章 第1031章 901 | 605
这个查询也可以动态生成:
Run Code Online (Sandbox Code Playgroud)DECLARE @cols nvarchar(max); SET @cols = STUFF((SELECT ', ' + ('SUM(CASE WHEN cDay = ' + FORMAT(cDay, 'D') + ' THEN DATEDIFF(ss, cl.StartTime, cl.EndTime) END) AS ' + CASE cDayName WHEN 'Saturday' THEN QUOTENAME('20-40 ' + FORMAT(cDate, 'dd-MM')) WHEN 'Sunday' THEN QUOTENAME('5-40 ' + FORMAT(cDate, 'dd-MM')) END + ' ') FROM Calendar WHERE cDayName IN ('Saturday', 'Sunday') ORDER BY cDate FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 1, ''); DECLARE @cmd nvarchar(max); SET @cmd = 'SELECT cl.ObjectName, cl.IndexName, cl.IndexType, ' + @cols + 'FROM CommandLog cl INNER JOIN Calendar c ON cl.StartTime >= DATEADD(minute, 30, c.cDate) AND cl.StartTime < DATEADD(day, 1, c.cDate) WHERE cDayName IN (''Saturday'', ''Sunday'') GROUP BY cl.ObjectName, cl.IndexName, cl.IndexType'; /* REMOVE Warning: Null value is eliminated by an aggregate or other SET operation. */ SET ANSI_WARNINGS OFF; EXEC sp_executesql @cmd; GO
dbfiddle在这里