锯齿形局部最大值的每日总计
Daily totals for sawtooth pattern local maxima
我有多个可以临时重置的单调计数器。这些计数器在绘制时表现出锯齿状行为(但它们并不是严格增加的)。我想要一份月度报告,显示每个计数器每天最大值的总和。
到目前为止,我的策略是在计数器小于上一次计数器采样(也小于或等于下一次采样)的行上放置一个“1”。然后计算该列的 运行ning 总计以确定没有重置的序列。
然后我将每日间隔分组以计算当天每个系列的最大最小值,然后将这些部分相加以获得当天的总计。
我的工作正常,但 运行 需要大约 10 秒。执行计划显示了两大类:一种在 cteData 中,我认为另一种在 cteSeries 中。我觉得我应该可以消除其中一个,但我不知道该怎么做。
这段代码的结果是(我现在可以看到它实际上是跨区间边界跳过样本):
interval tagname total
2020-01-01 alpha 3
2020-01-01 bravo 4
2020-01-02 alpha 3
2020-01-02 bravo 4
IF OBJECT_ID('tempdb..#counter_data') IS NOT NULL
DROP TABLE #counter_data;
CREATE TABLE #counter_data(
t_stamp DATETIME NOT NULL
,tagname VARCHAR(32) NOT NULL
,val REAL NULL
PRIMARY KEY(t_stamp, tagname)
);
INSERT INTO #counter_data(t_stamp, tagname, val)
VALUES
('2020-01-01 04:00', 'alpha', 0)
,('2020-01-01 04:00', 'bravo', 0)
,('2020-01-01 08:00', 'alpha', 1)
,('2020-01-01 08:00', 'bravo', 1)
,('2020-01-01 12:00', 'alpha', 2)
,('2020-01-01 12:00', 'bravo', 2)
,('2020-01-01 16:00', 'alpha', 0)
,('2020-01-01 16:00', 'bravo', 3)
,('2020-01-01 20:00', 'alpha', 1)
,('2020-01-01 20:00', 'bravo', 4)
,('2020-01-02 04:00', 'alpha', 2)
,('2020-01-02 04:00', 'bravo', 5)
,('2020-01-02 08:00', 'alpha', 3)
,('2020-01-02 08:00', 'bravo', 6)
,('2020-01-02 12:00', 'alpha', 0)
,('2020-01-02 12:00', 'bravo', 7)
,('2020-01-02 16:00', 'alpha', 1)
,('2020-01-02 16:00', 'bravo', 8)
,('2020-01-02 20:00', 'alpha', 2)
,('2020-01-02 20:00', 'bravo', 9)
;
DECLARE @dateStart AS DATETIME = '2020-01-01';
DECLARE @dateEnd AS DATETIME = DATEADD(month, 2, @dateStart);
WITH cteData AS(
SELECT
t_stamp
,tagname
,val
,CASE
WHEN val < LAG(val) OVER(PARTITION BY tagname ORDER BY t_stamp)
AND val <= LEAD(val) OVER(PARTITION BY tagname ORDER BY t_stamp)
THEN 1
ELSE 0
END AS rn
FROM #counter_data
WHERE
t_stamp >= @dateStart AND t_stamp < @dateEnd
AND tagname IN(
'alpha'
,'bravo'
)
)
,cteSeries AS(
SELECT
CAST(t_stamp AS DATE) AS interval
,tagname
,val
,SUM(rn) OVER(PARTITION BY tagname ORDER BY t_stamp) AS series
FROM cteData
)
,cteSubtotal AS(
SELECT
interval
,tagname
,MAX(val) - MIN(val) AS subtotal
FROM cteSeries
GROUP BY interval, tagname, series
)
,cteGrandTotal AS(
SELECT
interval
,tagname
,SUM(subtotal) AS total
FROM cteSubtotal
GROUP BY interval, tagname
)
SELECT *
FROM cteGrandTotal
ORDER BY interval, tagname
这看起来像是一个缺口和孤岛问题。我认为您希望 lag()
获得“先前”值和条件总和来计算每日计数。
select
tag_name,
cast(t_stamp as date) t_date,
sum(case when val = lag_val + 1 the 1 else 0 end) total
from (
select
c.*,
lag(val) over(
partition by tagname, cast(t_stamp as date)
order by t_stamp
) lag_val
from #counter_data c
) c
group by tagname, cast(t_stamp as date)
order by t_date, tagname
我只是通过将它与前一行进行比较来计算每一行中计数器的增加量:
with cte
as
(
SELECT *,isnull(lag(val) over (partition by tagname order by t_stamp),0) as previousVal
FROM counter_data
)
SELECT cast(t_stamp as date),tagname, sum(case when val>previousVal then val-previousval else val end )
FROM cte
GROUP BY cast(t_stamp as date),tagname;
我有多个可以临时重置的单调计数器。这些计数器在绘制时表现出锯齿状行为(但它们并不是严格增加的)。我想要一份月度报告,显示每个计数器每天最大值的总和。
到目前为止,我的策略是在计数器小于上一次计数器采样(也小于或等于下一次采样)的行上放置一个“1”。然后计算该列的 运行ning 总计以确定没有重置的序列。
然后我将每日间隔分组以计算当天每个系列的最大最小值,然后将这些部分相加以获得当天的总计。
我的工作正常,但 运行 需要大约 10 秒。执行计划显示了两大类:一种在 cteData 中,我认为另一种在 cteSeries 中。我觉得我应该可以消除其中一个,但我不知道该怎么做。
这段代码的结果是(我现在可以看到它实际上是跨区间边界跳过样本):
interval tagname total 2020-01-01 alpha 3 2020-01-01 bravo 4 2020-01-02 alpha 3 2020-01-02 bravo 4
IF OBJECT_ID('tempdb..#counter_data') IS NOT NULL
DROP TABLE #counter_data;
CREATE TABLE #counter_data(
t_stamp DATETIME NOT NULL
,tagname VARCHAR(32) NOT NULL
,val REAL NULL
PRIMARY KEY(t_stamp, tagname)
);
INSERT INTO #counter_data(t_stamp, tagname, val)
VALUES
('2020-01-01 04:00', 'alpha', 0)
,('2020-01-01 04:00', 'bravo', 0)
,('2020-01-01 08:00', 'alpha', 1)
,('2020-01-01 08:00', 'bravo', 1)
,('2020-01-01 12:00', 'alpha', 2)
,('2020-01-01 12:00', 'bravo', 2)
,('2020-01-01 16:00', 'alpha', 0)
,('2020-01-01 16:00', 'bravo', 3)
,('2020-01-01 20:00', 'alpha', 1)
,('2020-01-01 20:00', 'bravo', 4)
,('2020-01-02 04:00', 'alpha', 2)
,('2020-01-02 04:00', 'bravo', 5)
,('2020-01-02 08:00', 'alpha', 3)
,('2020-01-02 08:00', 'bravo', 6)
,('2020-01-02 12:00', 'alpha', 0)
,('2020-01-02 12:00', 'bravo', 7)
,('2020-01-02 16:00', 'alpha', 1)
,('2020-01-02 16:00', 'bravo', 8)
,('2020-01-02 20:00', 'alpha', 2)
,('2020-01-02 20:00', 'bravo', 9)
;
DECLARE @dateStart AS DATETIME = '2020-01-01';
DECLARE @dateEnd AS DATETIME = DATEADD(month, 2, @dateStart);
WITH cteData AS(
SELECT
t_stamp
,tagname
,val
,CASE
WHEN val < LAG(val) OVER(PARTITION BY tagname ORDER BY t_stamp)
AND val <= LEAD(val) OVER(PARTITION BY tagname ORDER BY t_stamp)
THEN 1
ELSE 0
END AS rn
FROM #counter_data
WHERE
t_stamp >= @dateStart AND t_stamp < @dateEnd
AND tagname IN(
'alpha'
,'bravo'
)
)
,cteSeries AS(
SELECT
CAST(t_stamp AS DATE) AS interval
,tagname
,val
,SUM(rn) OVER(PARTITION BY tagname ORDER BY t_stamp) AS series
FROM cteData
)
,cteSubtotal AS(
SELECT
interval
,tagname
,MAX(val) - MIN(val) AS subtotal
FROM cteSeries
GROUP BY interval, tagname, series
)
,cteGrandTotal AS(
SELECT
interval
,tagname
,SUM(subtotal) AS total
FROM cteSubtotal
GROUP BY interval, tagname
)
SELECT *
FROM cteGrandTotal
ORDER BY interval, tagname
这看起来像是一个缺口和孤岛问题。我认为您希望 lag()
获得“先前”值和条件总和来计算每日计数。
select
tag_name,
cast(t_stamp as date) t_date,
sum(case when val = lag_val + 1 the 1 else 0 end) total
from (
select
c.*,
lag(val) over(
partition by tagname, cast(t_stamp as date)
order by t_stamp
) lag_val
from #counter_data c
) c
group by tagname, cast(t_stamp as date)
order by t_date, tagname
我只是通过将它与前一行进行比较来计算每一行中计数器的增加量:
with cte
as
(
SELECT *,isnull(lag(val) over (partition by tagname order by t_stamp),0) as previousVal
FROM counter_data
)
SELECT cast(t_stamp as date),tagname, sum(case when val>previousVal then val-previousval else val end )
FROM cte
GROUP BY cast(t_stamp as date),tagname;