因此,最初的任务是创建一个报告,该报告确定给定5分钟间隔内的并发事务处理次数,并每周提供最多并发转氨酶的高水位标记。我已经解决了问题,但查询趋向于在3个月的数据中运行3-3.5小时。这不是我真正希望的。
在本练习中,我只关注两列:
Transaction_Data.DateTime (Start of Transaction: e.g. 2018-01-01 23:59:59.999)
Transaction_Data.Duration (Integer seconds: e.g. 272)
此查询的棘手部分是我需要获取DateTime + Duration,并将其分为5分钟间隔。因此,如果时间戳是10:02:00,而持续时间是715秒(以10:13:55结束),那么我需要以10:00、10:05和10:10的间隔来计数事务。
要实现此目的,我使用CTE填充一个临时表,该表具有在相关时间范围内的所有间隔,然后使用交叉联接查询将事务划分为各个间隔。
以下是查询:
DECLARE @Times Table (DateTime DateTime)
DECLARE @StartDate AS DATETIME
, @EndDate AS DATETIME
SET @StartDate = '2018-06-01'
SET @EndDate = '2018-08-31 23:59:59.999';
WITH DateIntervalsCTE AS
(
SELECT 0 i, @StartDate AS Date
UNION ALL
SELECT i + 5, DATEADD(minute, i, @StartDate )
FROM DateIntervalsCTE
WHERE DATEADD(minute, i, @StartDate ) < @EndDate
)
INSERT INTO @Times (DateTime)
SELECT DISTINCT Date
FROM DateIntervalsCTE
OPTION(MAXRECURSION 32767);
select Convert(varchar(10),DateAdd(day,-DatePart(weekday,IntData.DateTime)+1,Convert(varchar(10),IntData.DateTime,101)),101) as 'WeekOf'
, Max(Count) as 'MaxConcur'
from
(select t.DateTime
, count(t.DateTime) as 'Count'
from Transaction_Detail TD
cross join @Times t
where t.DateTime
between
DateAdd(ss,-(((DatePart(mi,TD.DateTime)%5)*60)+DatePart(ss,TD.DateTime)),DateAdd(ms,-DatePart(ms,TD.DateTime),TD.DateTime))
and DateAdd(ss,TD.Duration,TD.DateTime)
group by
t.DateTime) as IntData
group by
Convert(varchar(10),DateAdd(day,-DatePart(weekday,IntData.DateTime)+1,Convert(varchar(10),IntData.DateTime,101)),101)
关于Where子句中的DateAdd内容,我试图将开始时间向下舍入到最接近的5分钟间隔,以便CrossJoin将与开始间隔匹配。
关于数据,由于数据是由第三方应用程序生成的,因此我无法更改任何有关结构的信息。通常,我认为整个SQL Server都是只读的,因此通常避免在其上创建任何类型的静态表。由于我为各种客户端支持许多此类数据库,因此理想的做法是可以将代码简单地粘贴到SSMS窗口中并执行。
关于性能问题,CTE部分本身运行足够快,但是查询运行的方式我希望将5分钟间隔的26k(3个月)与325k交易记录进行比较是我问题的真正根源。朋友之间的85亿操作是什么?
出于全面披露的目的,尽管我编写了很多t-sql并且已经使用了很多年,但这是我第一次使用CTE和CrossJoins。我完全有可能搞砸了一些东西,但一直无法检测到它,但是从我所做的诊断工作来看,它似乎报告得很准确,尽管效果不佳。
我希望此请求的对象是具有更多T-SQL知识的人,然后我指出一种更好的方法来完成我要完成的工作,而这要用几分钟而不是几小时来完成。虽然我不会拒绝改写的解决方案,但我很高兴能直接指出一种更好的技术。
如果您已读过本文,谢谢您的时间。
-J.V。
样本输入
DateTime Duration
2018-06-01 00:04:55.223 57
2018-06-01 00:04:56.223 58
2018-06-01 00:08:37.180 62
2018-06-01 00:08:37.180 62
2018-06-01 00:20:29.183 10
2018-06-01 00:28:38.423 0
2018-06-01 00:28:53.190 15
2018-06-01 00:31:52.690 195
2018-06-01 00:32:20.917 209
2018-06-01 00:32:54.690 756
注意:这只是输入外观的一个很小的例子。
示例输出
WeekOf MaxConcur
05/27/2018 101
06/03/2018 169
06/10/2018 189
06/17/2018 148
06/24/2018 186
07/01/2018 218
07/08/2018 222
07/15/2018 210
07/22/2018 219
07/29/2018 225
08/05/2018 243
08/12/2018 231
08/19/2018 253
08/26/2018 220
最终解决方案
首先,感谢所有答复。这对我来说真是太棒了,我学到了一些有趣的想法来解决SQL问题。特别要感谢KumarHarsh使我足够接近以达到最终分辨率,从而在15秒内显示数据,这比我期望的要快得多。这是最终的查询(如果我没有正确执行此操作,很抱歉,但我认为需要共享最终答案):
-- Set Up Time Range
declare @minDate DateTime='2018-06-01 00:00:00.000'
declare @maxDate DateTime='2018-08-31 23:59:59.999'
-- Build Temporary Interval Table
create table #TimesTable (
[DateTime] DateTime not null
, [DateCol] Date not null
)
-- Populate Interval Table (5min Intervals)
insert into #TimesTable
select dateadd(minute,(RowNum-1)*5,@minDate) as 'DateTime'
, cast(dateadd(minute,(RowNum-1)*5,@minDate) as Date) as 'Date'
from (
select ROW_NUMBER()over(order by (select null)) as 'RowNum'
from master..spt_values a
CROSS JOIN master..spt_values b
) as TT
where cast(dateadd(minute,RowNum*5,@minDate) as DateTime) < @maxDate
-- Build Table Indexes
create clustered index ix_datecol on #TimesTable ([DateTime],[DateCol])
-- Query the Data
select Convert(varchar(10),DateAdd(day,-DatePart(weekday,IntData.DateTime)+1,Convert(varchar(10),IntData.DateTime,101)),101) as 'WeekOf'
, Max(Count) as 'MaxConcur'
from (
select TData.DateTime
, TData.[DateCol]
, Count(TData.DateTime) as 'Count'
from dbo.Transaction_Detail TD
outer apply (
select TT.DateTime
, TT.[DateCol]
from #TimesTable as TT
where TT.DateTime
between
TD.DateTime and DateAdd(ss,TD.Duration,TD.DateTime)
) as TData
group by TData.DateTime,TData.[DateCol]
) as IntData
where Convert(varchar(10),DateAdd(day,-DatePart(weekday,IntData.DateTime)+1,Convert(varchar(10),IntData.DateTime,101)),101) is not null
group by Convert(varchar(10),DateAdd(day,-DatePart(weekday,IntData.DateTime)+1,Convert(varchar(10),IntData.DateTime,101)),101)
order by 'WeekOf'
drop table #TimesTable
从Kumar的例子中,我必须进行一些更改:
答案 0 :(得分:1)
关于您的脚本,
为什么要在CTE中使用distinct,将其删除或您的CTE不好
不要使用表变量,请使用临时表
按“ Convert(varchar(10),DateAdd(day,-DatePart(weekday,IntData.DateTime)+1,Convert(varchar(10),IntData.DateTime,101)),101)
”分组
分组依据可以替换为,注意(DatePart(weekday,getdate())+1)
select DateAdd(day,-(DatePart(weekday,getdate())+1),Convert(varchar(10),getdate(),101))
select DateAdd(day,-(DatePart(weekday,getdate())+1),cast(getdate() as date))
我不确定,但它确实表明这部分是长而错误的。
仅执行此更改并检查。
我的方式
首先创建时间表表。这是一次时间表的创建。
您可以按照任意方式创建
declare @minDate Datetime='2005-01-01'
create table TimesTable ([DateTime] DateTime not null ,[DateCol] Date not null)
insert into TimesTable
select dateadd(minute,rn*5,@minDate),cast(dateadd(minute,rn*5,@minDate) as date)
from
(
select a.number, ROW_NUMBER()over(order by (select null))rn
from master..spt_values a
CROSS JOIN master..spt_values b
)t4
Create clustered index ix_datecol on TimesTable ([DateTime],[DateCol])
-其中“ 2005-01-01”是任何最小值,请根据您的要求选择最小值
如果索引不起作用并且脚本显示一些改进,则可以更改。
DECLARE @StartDate AS DATETIME
, @EndDate AS DATETIME
SET @StartDate = '2018-06-01'
SET @EndDate = '2018-08-31 23:59:59.999';
select Convert(varchar(10),DateAdd(day,-DatePart(weekday,IntData.DateTime)+1,Convert(varchar(10),IntData.DateTime,101)),101) as 'WeekOf'
, Max(Count) as 'MaxConcur'
from
(select t.DateTime,t.[DateCol]
, count(t.DateTime) as 'Count'
from dbo.Transaction_Detail TD
outer apply(select t.DateTime,t.[DateCol] from TimesTable t
where t.DateTime
between
DateAdd(ss,-(((DatePart(mi,TD.DateTime)%5)*60)+DatePart(ss,TD.DateTime)),DateAdd(ms,-DatePart(ms,TD.DateTime),TD.DateTime))
and DateAdd(ss,TD.Duration,TD.DateTime)
)t
group by
t.DateTime,t.[DateCol] ) as IntData
group by
t.[DateCol]
-- In place of cross join ,try OUTER APPLY once
我的脚本在开始时会抛出错误或给出错误的输出。但是我确信它可以纠正。
了解t。[DateCol]背后的想法,并相应地调整查询。
让我知道性能是否正常。
答案 1 :(得分:0)
尝试预先计算CTE中的所有内容。例如[WeekOf]
值select Convert(varchar(10),DateAdd...
应该来自CTE /表变量
在GROUP BY
和WHERE
中避免/最小化计算,例如where t.DateTime between DateAdd ....
应该只是一个简单的范围条件。
您不需要CROSS JOIN
DECLARE @Times Table (IntervalStart DateTime, IntervalEnd DateTime, [WeekOf] DATETIME)
DECLARE @StartDate AS DATETIME
, @EndDate AS DATETIME
SET @StartDate = '2018-06-01'
SET @EndDate = '2018-08-31 23:59:59.999';
WITH DateIntervalsCTE AS
(
SELECT 0 i, @StartDate AS Date
UNION ALL
SELECT i + 5, DATEADD(minute, i, @StartDate )
FROM DateIntervalsCTE
WHERE DATEADD(minute, i, @StartDate ) < @EndDate
)
INSERT INTO @Times (IntervalStart, IntervalEnd, [WeekOf])
SELECT Date, DATEADD(minute, 5, Date ), Convert(varchar(10),DateAdd(day,-DatePart(weekday,Date)+1,Convert(varchar(10),Date,101)),101)
FROM DateIntervalsCTE
OPTION(MAXRECURSION 32767);
SELECT [WeekOf], MAX( [Count] ) AS 'MaxConcur'
FROM(
SELECT t.IntervalStart, COUNT(t.IntervalStart) AS [Count], [WeekOf]
FROM Transaction_Detail AS TD
INNER join @Times AS t ON t.IntervalStart <= TD.DateTime AND DATEADD( ss, TD.Duration, TD.DateTime ) < t.IntervalEnd
GROUP BY [WeekOf], t.IntervalStart ) AS IntData
GROUP BY [WeekOf]
我添加了PeriodEnd
列以简化连接条件(请参阅第2点。)
我已将WeekOf
列计算添加到CTE(请参阅第1点)。
如果Transaction_Data.DateTime
上有索引,您可以尝试在嵌套的WHERE
中添加SELECT
子句,例如WHERE @StartDate <= TD.DateTime AND TD.DateTime <= @EndDate
减少搜索到的交易记录的数量。
以我的经验,使用合理的硬件,此查询的时间不应超过20分钟。
尝试使用较小范围的@StartDate
和@EndDate
进行测试