我有一个包含以下信息的表:
Conf_Start_Time
Part_Start_Time
Part_End_Time
如果t介于Part_Start_Time
和Part_End_Time
之间,则记录在时间t被视为有效。
我想要做的是分析所有记录,以确定在指定日期内有多少记录处于活动状态。我建议的解决方案是在当天循环每一分钟(比如从早上6点到晚上9点)并检查当天的每条记录,以确定用户是否在指定时间有效,t。
在SQL中是否有解决方案,或者我应该继续使用代码解决方案吗?
在代码中,我会将所有记录拉到内存中,循环时间(早上6点到晚上9点)并在指定日期测试每条记录以确定它是否在当前时间处于活动状态。如果它处于活动状态,我会增加一个计数器,如果没有,则继续下一个记录。下一次,重新初始化计数器并继续循环。
我们正在使用SQL Server 2005。
更新:我正在寻找的输出是从早上6点到晚上9点的最大并发使用数组
Record Conf_Start_Time Part_Start_Time Part_End_Time
1. 6/5/2012 13:40:00 6/5/2012 13:41:23 6/5/2012 13:45:27
2. 6/5/2012 13:40:00 6/5/2012 13:40:23 6/5/2012 13:47:29
3. 6/5/2012 13:40:00 6/5/2012 13:42:55 6/5/2012 13:44:17
所以在时间13:40:00 0记录是活跃的;在时间13:41:00 1记录是活跃的;在时间13:42:00 2记录是活跃的;在时间13:43:00 3条记录是活跃的;
我需要当天每分钟的数据。然后是每个月的每一天。这种类型的循环甚至可以在SQL中完成吗?
答案 0 :(得分:2)
如果您想要所有在2012年8月7日生效的记录,请执行以下操作:
select * from your_table
where '2012-08-07' between Part_Start_Time and Part_End_Time
答案 1 :(得分:0)
试试这个:
DECLARE @auxDate datetime
SELECT *
FROM your_table
WHERE @auxDate BETWEEN Part_Start_Time AND Part_End_Time
Between子句是包含性的,如果您不想包含某些日期,请考虑使用:
DECLARE @auxDate datetime
SELECT *
FROM your_table
WHERE @auxDate >= Part_Start_Time
AND @auxDate <= Part_End_Time
答案 2 :(得分:0)
以下使用相关子查询来获取所需的数字。我们的想法是计算累计开始次数和累积结束次数,直到每次:
with alltimes as
(select t.*
from ((select part_start_time as thetime, 1 as IsStart, 0 as IsEnd
from t
) union all
(select part_end_time, 0 as isStart, 1 as IsEnd
from t
)
) t
)
select t.*,
(cumstarts - cumends) as numactive
from (select alltimes.thetime,
(select sum(isStart)
from allStarts as where as.part_start_time <= alltimes.thetime
) as cumStarts,
(select sum(isEnd)
from allStarts as where as.part_end_time <= alltimes.thetime
) as cumEnds
from alltimes
) t
输出基于数据中的每个时间。
根据经验,您不希望在应用程序端执行大量数据工作。如果可能,最好在数据库中完成。
当有多个启动和同时结束时,此查询将具有重复项。在这种情况下,您需要确定如何处理此案例。但是,这个想法是一样的。外部选择将是:
select t.thetime, max(cumstarts - cumends) as numactives
你需要一个group by子句:
group by t.thetime
“max”给出了开始优先级(意味着具有相同的时间戳,开始被视为先发生,因此您获得当时的最大活动数)。 “Min”会优先考虑结束。并且,如果您使用平均值,请记住转换为浮点数:
select t.thetime, avg(cumstarts*1.0 - cumends) as avgnumactives
答案 3 :(得分:0)
这就是我解决问题的方法。
我要做的第一件事就是创建一个如下所示的序列表。由于各种原因,在SQL中具有基本上无限(或至少是大的)数字序列非常有用。像这样:
create table dbo.sequence
(
seq_no int not null primary key clustered ,
)
declare @v int
set @v = -100000
while @v <= 100000
begin
insert dbo.sequence values ( @v )
set @v = @v+1
end
实际上,我使用批量复制以不同的方式播种表,甚至编写CLR表值函数来生成所需的范围。上述查询在加载表时不会表现出理想的性能特征。
一旦我有类似的东西,我会写一个如下的查询。它会为您提供完整的报告,列出指定报告期内每天所需的报告存储桶。通过设置适当的变量可以调整所有内容。如果您想要稀疏报告,请将最终left join
更改为标准内部联接。
免责声明:此代码尚未经过测试,但它类似于我编写的用于执行相同操作的代码。这种方法很合理,但代码本身可能包含错误。
-----------------------------------------------------------------------------
-- define the range of days in which we are interested
-- it might well be more than 1, but for this example, we'll define the start
-- and end days as the same, so we are interested in just one day.
-----------------------------------------------------------------------------
declare @dtFrom datetime
declare @dtThru datetime
set @dateFrom = '2012-06-01'
set @dateThru = '2012-06-01'
------------------------------------------------------------------------------
-- the next thing in which we are interested in are the boundaries of
-- the time period in which we are interested, and the interval length
-- of each reporting bucket, in minutes.
--
-- For this example, we're interesting in the time period
-- running from 6am through 9pm, such that 6am >= x < 9pm.
--
-- We also need a value defining start-of-day (midnight).
--
-- Setting a datetime value to '' will give you the epoch: 1900-01-01 00:00:00.000
-- Setting a datetime value to just a time-of-day string literal will
-- give you the epoch day at the desired time, so '06:00:00' converts to
-- '1900-01-01 06:00:00'. Crazy, but that's SQL Server.
--
------------------------------------------------------------------------------
declare @start_of_day datetime
declare @timeFrom datetime
declare @timeThru datetime
declare @interval_length_in_minutes int
set @start_of_day = '00:00:00'
set @timeFrom = '06:00:00'
set @timeThru = '21:00:00'
set @interval_length_in_minutes = 15
------------------------------------------------------------------------------
--
-- On to the meat of the matter. This query has three parts to it.
--
-- 1. Generate the set of reporting days, using our sequence table
-- 2. Generate the set of reporting buckets for each day, again, using our sequence table
--
-- The left join of these two virtual tables produces the set of all reporting periods
-- that we will use to match up to the source data that will fill the report.
--
-- 3. Finally, assign each row to 0 or more reporting buckets.
-- A given record has a time range in which it was 'active'.
-- Consequently, it may fall into multiple reporting buckets, and hence,
-- the comparison is a little wonky: A record is assigned to a reporting bucket
-- if both of these are true for the data record:
--
-- * Its active period ended *on or after* the start of the reporting period/bucket.
-- * Its active period began *on or before* the end of the reporting period.
--
-- It take a while to get your head around that, but it works.
--
-- When all that is in place, we use GROUP BY and the aggregate function SUM()
-- to collapse each reporting bucket into a single row and compute the active count.
-- We use SUM() in preference to COUNT() as we want a full report,
-- so we use left joins. Unlike other aggregate functions, COUNT() does not
-- exclude null rows/expressions in its computation.
--
-- There you go. Easy!
--
-----------------------------------------------------------------------------------
select timeFrom = dateadd(minute, times.offset , days.now ) ,
timeThru = dateadd(minute, times.offset + @interval_length_in_minutes , days.now ) ,
N = sum( case when t.id is null then 0 else 1 end ) -- we sum() here rather than count() since we don't want missing rows from dbo.myFunkyTable to increment the count
from ( select now = dateadd(day, seq_no , @dateFrom ) -- get the set of 'interesting' days
from dbo.sequence -- via our sequence table
where seq_no >= 0 --
and seq_no < datediff(day,@dateFrom,@dateThru) --
) days --
left join ( select offset = seq_no -- get the set of time buckets
from dbo.sequence -- each bucket is defined by its offset
where seq_no >= datediff(minute,@start_of_day,@timeFrom) -- as defined in minutes-since-start-of-day
and seq_no < datediff(minute,@start_of_day,@timeThru) -- and is @interval_length_in_minuts long
and 0 = seq_no % @interval_length_in_minutes --
) times
left join dbo.myFunkyTable t on t.Part_StartTime < dateadd(minute, times.offset + @interval_length_in_minutes , days.now )
and t.Part_EndTime >= dateadd(minute, times.offset , days.now )
group by dateadd(minute, times.offset , days.now ) ,
dateadd(minute, times.offset + @interval_length_in_minutes , days.now )
order by 1 , 2