我正在数据库中执行一些计划优化。
对于工作计划,许多记录如下所示
Time NextRunTime
Every 3 hours 2019-06-03 10:00:00
Every 3 hours 2019-05-28 20:00:00
Every 4 hours 2017-07-31 18:00:00
Every 1 hours 2019-06-03 14:00:00
Every 4 hours 2017-06-08 16:00:00
什么是在24小时之内将“每条”记录拆分为单独记录的有效方法?
例如,对于第一条记录(从10:00开始每隔3小时),我需要将以下内容插入到表中。
Time
13:00:00
16:00:00
19:00:00
22:00:00
01:00:00
04:00:00
07:00:00
10:00:00
对于第一个表中的每个记录,我都需要使用“每个”重复此操作。
有人可以帮忙吗?
答案 0 :(得分:1)
如果要正确执行此操作,则需要tally table。首先,让我们看一下解决此问题所需的逻辑。
DECLARE @starttime DATETIME = '2019-06-03 10:00:00', @hours INT = 3;
SELECT t.N, Tm = CAST(DATEADD(HOUR,t.N*3,@startTime) AS TIME)
FROM
(
SELECT TOP (24/@hours) ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS a(x),
(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS b(x)
) AS t(N);
返回:
N Tm
------- ----------------
1 13:00:00.0000000
2 16:00:00.0000000
3 19:00:00.0000000
4 22:00:00.0000000
5 01:00:00.0000000
6 04:00:00.0000000
7 07:00:00.0000000
8 10:00:00.0000000
现在提供一些示例数据和一个记录标识符(名为“ someId”),以便我们可以为表中的所有行进行计算。
-- Sample Data
DECLARE @yourTable TABLE (someId INT IDENTITY PRIMARY KEY, freq INT, NextRunTime DATETIME);
INSERT @yourTable(freq, NextRunTime) VALUES (3, '2019-06-03 10:00:00'),
(3, '2019-05-28 20:00:00'),(4, '2017-07-31 18:00:00'),
(1, '2019-06-03 14:00:00'),(4, '2017-06-08 16:00:00');
-- Solution
SELECT yt.someId, f.Tm
FROM @yourTable AS yt
CROSS APPLY
(
SELECT t.N, CAST(DATEADD(HOUR,t.N*yt.freq,yt.NextRunTime) AS TIME)
FROM
(
SELECT TOP (24/yt.freq) ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS a(x),
(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS b(x)
) AS t(N)
) AS f(N,Tm);
返回:
someId Tm
----------- ----------------
1 13:00:00.0000000
1 16:00:00.0000000
1 19:00:00.0000000
1 22:00:00.0000000
1 01:00:00.0000000
1 04:00:00.0000000
1 07:00:00.0000000
1 10:00:00.0000000
2 23:00:00.0000000
2 02:00:00.0000000
2 05:00:00.0000000
2 08:00:00.0000000
2 11:00:00.0000000
2 14:00:00.0000000
2 17:00:00.0000000
2 20:00:00.0000000
3 22:00:00.0000000
3 02:00:00.0000000
3 06:00:00.0000000
3 10:00:00.0000000
3 14:00:00.0000000
3 18:00:00.0000000
4 15:00:00.0000000
4 16:00:00.0000000
4 17:00:00.0000000
4 18:00:00.0000000
4 19:00:00.0000000
4 20:00:00.0000000
4 21:00:00.0000000
4 22:00:00.0000000
4 23:00:00.0000000
4 00:00:00.0000000
4 01:00:00.0000000
4 02:00:00.0000000
4 03:00:00.0000000
4 04:00:00.0000000
4 05:00:00.0000000
4 06:00:00.0000000
4 07:00:00.0000000
4 08:00:00.0000000
4 09:00:00.0000000
4 10:00:00.0000000
4 11:00:00.0000000
4 12:00:00.0000000
4 13:00:00.0000000
4 14:00:00.0000000
5 20:00:00.0000000
5 00:00:00.0000000
5 04:00:00.0000000
5 08:00:00.0000000
5 12:00:00.0000000
5 16:00:00.0000000
答案 1 :(得分:1)
我一直认为递归cte是解决您的查询的好方法:
-- the sample data
declare @data as table (freq varchar(100), tmst datetime, each_ int)
insert into @data
select s.freq,s.tmst,
convert(int,replace(replace(freq,'Every ',''),' hours','')) as each_
from (
select 'Every 4 hours' as freq, convert(datetime,'2019-06-02 10:00:00') as tmst union all
select 'Every 3 hours' as freq, convert(datetime,'2019-06-02 11:00:00') as tmst union all
select 'Every 2 hours' as freq, convert(datetime,'2019-06-02 10:00:00') as tmst
) s
-- the query
;with cte as (
select freq, tmst, each_, null t1 from @data
union all
select freq, tmst, each_, isnull(t1,datepart(hour,tmst)) + each_
from cte
where isnull(t1,datepart(hour,tmst)) + each_ <= 23
)
select freq,
isnull(convert(datetime, convert(varchar(8),tmst,112) + ' ' + (convert(varchar(100),t1) + ':00:00' ), 120),tmst)
from cte
order by 1, 2
使用第二个版本,您可以获得从0到23的所有范围(在上一个示例中,您只是从起点到23)
-- the query
;with findfirst as (
select freq, tmst, datepart(hour,tmst) as fhour, datepart(hour,tmst) as init, each_ from @data
union all
select freq, tmst, fhour, init - each_, each_ from findfirst where init - each_ >= 0
),
cte as (
select min(init) as init, freq, tmst, each_, fhour from findfirst group by freq, tmst, each_, fhour
union all
select init + each_, freq, tmst, each_, fhour from cte where init + each_ <= 23
)
select freq,tmst,convert(time, right('0' + convert(varchar(2),init), 2) + ':00:00')
from cte order by freq,init,each_
请记住,继续使用@data
表。
输出:
答案 2 :(得分:0)
首先删除所有具有要创建的临时表名称的临时表:
IF OBJECT_ID(N'tempdb..#Interval', N'U') IS NOT NULL DROP TABLE #Interval
GO
IF OBJECT_ID(N'tempdb..#Interval2', N'U') IS NOT NULL DROP TABLE #Interval2
GO
IF OBJECT_ID(N'tempdb..#Runstart', N'U') IS NOT NULL DROP TABLE #Runstart
GO
然后创建临时表并将数据插入到临时表中
CREATE TABLE #Interval
(
_Time NVARCHAR(13),
NextRunTime DATETIME
)
GO
INSERT INTO #Interval VALUES ('Every 3 hours','2019-06-03 10:00:00')
INSERT INTO #Interval VALUES ('Every 3 hours','2019-05-28 20:00:00')
INSERT INTO #Interval VALUES ('Every 4 hours','2017-07-31 18:00:00')
INSERT INTO #Interval VALUES ('Every 1 hours','2019-06-03 14:00:00')
INSERT INTO #Interval VALUES ('Every 4 hours','2017-06-08 16:00:00')
GO
CREATE TABLE #Interval2
(
RunID INT IDENTITY(10001,1) NOT NULL PRIMARY KEY,
_Time INT NOT NULL,
NextRunTime DATETIME NOT NULL
)
GO
下面的代码可确保您每次启动都获得正确的间隔,请注意:如果小时数等于或大于10,则有必要在此处添加更多代码以使选择的位数取决于条件包含它们的字符串的长度,请告诉我是否也需要此代码。
INSERT INTO #Interval2 (_TIME,NextRunTime) SELECT SUBSTRING(_Time,7,1),NextRunTime FROM #Interval WHERE LEFT(_Time,5) = 'Every'
GO
CREATE TABLE #Runstart
(
StartID INT IDENTITY(10001,1) NOT NULL PRIMARY KEY,
RunID INT NOT NULL,
[Start_DTTM] DATETIME
)
GO
接下来,使用此循环填充#RUNSTART表:
DECLARE @RunID INT = 10001
DECLARE @RunTime INT = (SELECT _TIME FROM #Interval2 WHERE RunID = @RunID)
DECLARE @NextRun DATETIME = (SELECT NextRunTime FROM #Interval2 WHERE RunID = @RunID)
WHILE @RunID <= (SELECT MAX(RunID) FROM #Interval2)
BEGIN
WHILE @NextRun < (SELECT DATEADD(DD,1,NextRunTime) FROM #Interval2 WHERE RunID = @RunID)
BEGIN
INSERT INTO #Runstart (RunID,[Start_DTTM]) SELECT @RunID,DATEADD(HH,@RunTime,@NextRun)
SET @NextRun = (SELECT DATEADD(HH,@RunTime,@NextRun))
END
SET @RunID = @RunID+1
SET @RunTime = (SELECT _TIME FROM #Interval2 WHERE RunID = @RunID)
SET @NextRun = (SELECT NextRunTime FROM #Interval2 WHERE RunID = @RunID)
END
GO
SELECT StartID, RunID,CONVERT(VARCHAR,START_DTTM,108) AS Start_time FROM #RUNSTART