What is the most efficient way to count the number of occurrences according?
I found the numbers table is the most efficient way to generate time sequence data based on start time and end time.
I have create a number table starts from 0 to 100,000.
I have generate time sequence table as following:
Declare #Start datetime = '2018-01-01 00:00:00.000',#End datetime ='2018-02-01 00:00:00.000';
SELECT
DATEADD(MINUTE,Number*15,#Start) StartTime,
[Number],
DATEADD(MINUTE,(Number+1)*15,#Start) EndTime
FROM dbo.Numbers
Where (Number+1)*15<=DATEDIFF(MINUTE,#Start,#End)
Order By Number;
I have table of data like:
Time ID
2018-01-01 00:00:01.000 1
2018-01-01 00:00:02.000 1
2018-01-01 00:15:00.000 124
2018-01-01 00:28:00.000 341
2018-01-01 00:26:00.000 111
2018-01-01 01:02:00.000 162
2018-01-01 04:09:00.000 110
2018-01-01 05:09:00.152 398
2018-01-01 08:12:00.000 902
2018-01-01 12:45:00.000 009
2018-01-01 13:23:00.000 000
2018-01-01 15:37:00.000 187
How can I count time based on 15 minutes interval?
You can try to use cte recursive to make a calendar table then do outer join
Declare #Start datetime = '2018-01-01 00:00:00.000',#End datetime ='2018-02-01 00:00:00.000';
;WITH CTE AS (
SELECT #Start startTime,DATEADD(MINUTE,15,#Start) nextTime,#End endTime
UNION ALL
SELECT DATEADD(MINUTE,15,startTime),DATEADD(MINUTE,15,nextTime) nextTime,#End
FROM CTE
WHERE DATEADD(MINUTE,15,startTime) < #End
)
SELECT startTime,nextTime,COUNT(t1.ID)
FROM CTE c1
LEFT JOIN T t1 on t1.Time BETWEEN c1.startTime and c1.nextTime
GROUP BY startTime,nextTime
option ( MaxRecursion 0 );
Note
The CTE default maximum recursion is 100, you can sett option ( MaxRecursion 0 );
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
Sqlfiddle
How about this. Just a normal group by so you can put in HAVING, SUM, AVERAGE etc too if you want. Run top section once:
create table TestTable
(
Time datetime,
ID int
)
GO
insert into TestTable values('2018-01-01 00:00:01.000' , 1)
insert into TestTable values('2018-01-01 00:00:02.000' , 1)
insert into TestTable values('2018-01-01 00:15:00.000' ,124)
insert into TestTable values('2018-01-01 00:28:00.000' ,341)
insert into TestTable values('2018-01-01 00:26:00.000' ,111)
insert into TestTable values('2018-01-01 01:02:00.000' ,162)
GO
CREATE FUNCTION [dbo].[RoundTime] (#Time datetime, #RoundTo float) RETURNS datetime
AS
BEGIN
DECLARE #RoundedTime smalldatetime, #Multiplier float
SET #Multiplier = 24.0 / #RoundTo
SET #RoundedTime= ROUND(CAST(CAST(CONVERT(varchar, #Time, 121) AS datetime) AS float) * #Multiplier, 0) / #Multiplier
RETURN #RoundedTime
END
GO
Then the actual working:
DECLARE #startDate DATETime
DECLARE #endDate DATETime
SET #startDate = '2018-01-01'
SET #endDate = GETDATE()
DECLARE #dateAxis TABLE
(
dt DATETime
)
DECLARE #currentDate DATETime = #startDate
WHILE #currentDate <= #endDate
BEGIN
INSERT INTO #dateAxis
SELECT #currentDate
SET #currentDate = DATEADD(Minute, 15, #currentDate)
END
-- axis table
--select * from #dateAxis
SELECT
dt AS joinDt,
dataset.MyCount
FROM
#dateAxis axis
LEFT JOIN
(
SELECT
dbo.RoundTime([Time], 0.5) AS joinDt,
count(*) AS MyCount
FROM
/*Your table here*/
TestTable tt
group by
dbo.RoundTime([Time], 0.5)
) dataset
ON dataset.joinDt = axis.dt
ORDER BY
axis.dt
Related
I have following:
DECLARE #TempTable TABLE
(
[Id] [int],
[FirstName] [varchar](40) NULL,
[Birthdate] [date] NULL
);
insert into #TempTable values (1, 'A', convert(date, '05/25/2017', 101))
insert into #TempTable values (2, 'B', convert(date, '06/25/2017', 101))
What I need is query which will return all Birthdays in range from StartDate to to EndDate.
To be more here is what I expect to get as a result:
Case 1:
If date range is set to:
DECLARE #StartDate datetime = '05/01/2017';
DECLARE #EndDate datetime = '07/01/2017';
Query should return:
1 A 2017-05-25
2 B 2017-06-25
Case 2:
If date range is set to:
DECLARE #StartDate datetime = '05/01/2017';
DECLARE #EndDate datetime = '06/01/2017';
Query should return:
1 A 2017-05-25
Case 3:
If date range is set to:
DECLARE #StartDate datetime = '05/01/2015';
DECLARE #EndDate datetime = '07/01/2017';
Query should return:
1 A 2017-05-25
1 A 2016-05-25
1 A 2015-05-25
2 B 2017-06-25
2 B 2016-06-25
2 B 2015-06-25
Case 4:
If date range is set to:
DECLARE #StartDate datetime = '05/01/2015';
DECLARE #EndDate datetime = '06/01/2017';
Query should return
1 A 2017-05-25
1 A 2016-05-25
1 A 2015-05-25
2 B 2016-06-25
2 B 2015-06-25
Firstly we creates all dates for given range . then apply logic
DECLARE #StartDate datetime = '05/01/2015';
DECLARE #EndDate datetime = '06/01/2017';
;With DateSequence as
(
Select #StartDate as Bdate
union all
Select dateadd(day, 1, Bdate)
from DateSequence
where Bdate < #EndDate
)
Select ID,FirstName,Bdate as BirthDate from DateSequence
cross join #TempTable
where Bdate between #StartDate and #EndDate and
month(Bdate)= month(BirthDate) and day(Bdate) = day(BirthDate)
order by ID asc , Bdate desc
option (MaxRecursion 2000)
OutPut :
At its most basic level, you can do this by creating a list of everyone's birthday for 100 years, then filtering...
with numbers as
(
select 0 as NN
union all
select NN+1
from numbers
where NN < 100
)
select id, dateadd(yy,NN,BirthDate) as Birthdays
from numbers
cross join #TempTable
where dateadd(yy,NN,BirthDate) between #StartDate and #EndDate
I have a datetime field in my SQL table. I have created a procedure that takes count as a variable and generates records for the table. If the count is 5 it will generate 5 records.The logic i want is that when i provide 5 as an input parameter the datetime field in the table should be autofilled with values
12/20/2015 9:00
12/20/2015 11:00
12/20/2015 13:00
12/20/2015 15:00
12/20/2015 17:00
So every time a record is inserted into a table,the 2 hours of time should be added.
Recursive CTEs are one way to create records on the fly. The key here is to create an anchor (this is the first SELECT inside the CTE, which is your starting point). And an exit check (which is the WHERE clause).
Read up on MAXRECURSION if you want to create more than 100 records at a time.
Example
DECLARE #RecordsRequired INT = 5;
DECLARE #BaseDateTime SMALLDATETIME = GETDATE();
WITH [Sample] AS
(
/* This CTE uses recursion to create the required number of
* records.
*/
SELECT
1 AS RowNumber,
#BaseDateTime AS [DateTime]
UNION ALL
SELECT
RowNumber + 1 AS RowNumber,
DATEADD(HOUR, 2, [DateTime]) AS [DateTime]
FROM
[Sample]
WHERE
RowNumber < #RecordsRequired
)
SELECT
RowNumber,
[DateTime]
FROM
[Sample]
;
You could also look into WHILE blocks.
Use this code:
------------------ INPUT ------------------------
declare #start_date datetime = '01/01/2000 14:00'
declare #loops int = 5
-------------------------------------------------
declare #i int = 0
while (#i < #loops) begin
select dateadd(hour, #i * 2, #start_date)
set #i = #i + 1
end
Try this without LOOP
Declare #count int = 5,
#incrementer int =2 -- in case if you want to change the incrementer
SELECT Dateadd(hh, num * #incrementer, dates)
FROM (SELECT Cast(CONVERT(VARCHAR(20), Dateadd(dd, 1, Getdate()), 111)
+ ' 9:00 AM' AS DATETIME) AS Dates,
num
FROM (VALUES(0),(1),(2),(3),(4),(5)) TC (num)) A
WHERE num <= #count - 1
SQL FIDDLE DEMO
Please find the sample code below, it contains the logic that you needed. Hope it helps!!
--Create a temp table for sample output
CREATE TABLE #temp
(
CreatedDate datetime
)
--Declaring variables
DECLARE #Count int
DECLARE #TimeCounter int
--intializing values
SET #Count=5
SET #TimeCounter=0
WHILE(#Count>0)
BEGIN
--SELECT getdate()+1
insert into #temp(#temp.CreatedDate) Select DATEADD(hour,#TimeCounter,getdate())
SET #TimeCounter=#TimeCounter+2
SET #Count=#Count-1
END
--Final values
SELECT * FROM #temp tmp
--Dropping table
DROP TABLE #temp
This is one of those problems that's best solved with a numbers table / function. Much less code than recursion or loops, usually faster for anything non-trivial and more reusable too.
The core code you want is
CREATE PROCEDURE usp_PopulateAppointments
(
#StartDateTime datetime2(3),
#Records int,
#Interval int = 120 --Time between appointment slots in minutes. Default to 2h if not manually specified.
)
INSERT INTO Appointments
SELECT
DATEADD(m, #Interval * Number, #StartDateTime)
FROM dbo.udfNumbers(0, #Recs)
I've assumed in this a numbers function that takes #StartAt and #NumberResults. I use one derived from Adam's final code in the comments on http://dataeducation.com/you-require-a-numbers-table/ - in my experience it's faster than a real table, and takes less space too.
Create Table dates
(
datetimefield datetime not null
)
go
Create Procedure FillDateTimeField
#insertxrows int
AS
begin
Declare #LastDateTimeInserted as datetime
set #LastDateTimeInserted = (select isnull(max(datetimefield),getdate()) from Dates)
;WITH norows AS (
SELECT 1 as num, Dateadd(hour,2,#LastDateTimeInserted) as FirstRecord
UNION ALL
SELECT num + 1, dateadd(hour,2,firstrecord) FROM
norows
WHERE num < #insertxrows
)
insert into dates
select firstrecord from norows
end
I want to show all dates between two dates when there is any date data missing then its should show zero in val column .
declare #temp table (
id int identity(1,1) not null,
CDate smalldatetime ,
val int
)
INSERT STATEMENT FOR DATA TO CHECK
insert into #temp select '10/2/2012',1
insert into #temp select '10/3/2012',1
insert into #temp select '10/5/2012',1
insert into #temp select '10/7/2012',2
insert into #temp select '10/9/2012',2
insert into #temp select '10/10/2012',2
insert into #temp select '10/13/2012',2
insert into #temp select '10/15/2012',2
Retrieve records between first day of month and today
select * from #temp where CDate between '10/01/2012' AND '10/15/2012'
As i run this query its show me all data between these two dates but i want to also include missing dates with val=0
SQL FIDDLE WITH SAMPLE DATA
;with d(date) as (
select cast('10/01/2012' as datetime)
union all
select date+1
from d
where date < '10/15/2012'
)
select t.ID, d.date CDate, isnull(t.val, 0) val
from d
left join temp t
on t.CDate = d.date
order by d.date
OPTION (MAXRECURSION 0) -- use this if your dates are >99 days apart
You need to make up the dates, so I've use a recursive common table expression here.
SQL Fiddle
MAXRECURSION number
Specifies the maximum number of recursions allowed for this query. number is a nonnegative
integer between 0 and 32767. When 0 is specified, no limit is applied. If this option is
not specified, the default limit for the server is 100.
When the specified or default number for MAXRECURSION limit is reached during query
execution, the query is ended and an error is returned.
This will work as long as there are less than 2047 days between from and to dates
declare #from smalldatetime = '10/01/2012'
declare #to smalldatetime = '10/15/2012'
select t.id, dateadd(day, number,#from), isnull(val, 0) val from #temp t
right join master..spt_values s
on dateadd(d, s.number, #from) = t.CDate
where
datediff(day, #from, #to ) > s.number
and s.type = 'P'
I think the best way to do this is to create your own table with dates (you can also use master.dbo.spt_values, but I personally don't like that solution)
declare #Temp_Dates table (CDate datetime)
declare #Date datetime
select #Date = (select min(CDate) from temp)
while #Date <= (select max(CDate) from temp)
begin
insert into #Temp_Dates (CDate)
select #Date
select #Date = dateadd(dd, 1, #Date)
end
select D.CDate, isnull(T.id, 0) as id
from #Temp_Dates as D
left outer join temp as T on T.CDate = D.CDate
you can also use recursive solution with CTE
DECLARE #min DATETIME,
#max DATETIME,
#val INT
SELECT #min = Min(CDATE),
#max = Max(CDATE)
FROM TEMP
DECLARE #temp TABLE
(
CDATE SMALLDATETIME,
VAL INT
)
WHILE #min < #max
BEGIN
SELECT #val = VAL
FROM TEMP
WHERE CDATE = #min
INSERT #temp
VALUES (#min,
#val)
SET #min = Dateadd(D, 1, #min)
SET #val = 0
END
SELECT *
FROM #temp
Declare #temp Table(id int identity(1,1) not null,CDate smalldatetime ,val int)
insert into #temp select '10/2/2012',1
insert into #temp select '10/3/2012',1
insert into #temp select '10/5/2012',1
insert into #temp select '10/7/2012',2
insert into #temp select '10/9/2012',2
insert into #temp select '10/10/2012',2
insert into #temp select '10/13/2012',2
insert into #temp select '10/15/2012',2
DECLARE #startDate DATE= '10/01/2012'
DECLARE #endDate DATE= '10/15/2012'
SELECT t.Id, X.[Date],Val = COALESCE(t.val,0)
FROM
(SELECT [Date] = DATEADD(Day,Number,#startDate)
FROM master..spt_values
WHERE Type='P'
AND DATEADD(day,Number,#startDate) <= #endDate)X
LEFT JOIN #temp t
ON X.[Date] = t.CDate
using a recursive cte with min and max
declare #T table (id int identity(1,1) primary key, dt date not null, val int not null);
insert into #T (dt, val) values
('10/2/2012',1)
, ('10/3/2012',1)
, ('10/5/2012',1)
, ('10/7/2012',2)
, ('10/9/2012',2)
, ('10/10/2012',2)
, ('10/13/2012',2)
, ('10/15/2012',2);
--select * from #T;
with cte as
( select min(dt) as dt, max(dt) as mx
from #T
union all
select dateadd(dd, 1, dt), mx
from CTE
where dt < mx
)
select c.dt, isnull(t.val, 0) as val
from cte c
left join #T t
on c.dt = t.dt
order by c.dt
option (maxrecursion 0);
dt val
---------- -----------
2012-10-02 1
2012-10-03 1
2012-10-04 0
2012-10-05 1
2012-10-06 0
2012-10-07 2
2012-10-08 0
2012-10-09 2
2012-10-10 2
2012-10-11 0
2012-10-12 0
2012-10-13 2
2012-10-14 0
2012-10-15 2
What is the easiest way to populate a temp table with dates including and between 2 date parameters. I only need the 1st day of the month dates.
So for example if #StartDate = '2011-01-01' and #EndDate = '2011-08-01'
Then I want this returned in the table
2011-01-01
2011-02-01
2011-03-01
2011-04-01
2011-05-01
2011-06-01
2011-07-01
2011-08-01
This works even if the #StartDate is not the first of the month. I'm assuming that if it's not the start of the month, you want to begin with the first of the next month. Otherwise remove the +1.:
;WITH cte AS (
SELECT CASE WHEN DATEPART(Day,#StartDate) = 1 THEN #StartDate
ELSE DATEADD(Month,DATEDIFF(Month,0,#StartDate)+1,0) END AS myDate
UNION ALL
SELECT DATEADD(Month,1,myDate)
FROM cte
WHERE DATEADD(Month,1,myDate) <= #EndDate
)
SELECT myDate
FROM cte
OPTION (MAXRECURSION 0)
declare #StartDate date = '2014-01-01';
declare #EndDate date = '2014-05-05';
;WITH cte AS (
SELECT #StartDate AS myDate
UNION ALL
SELECT DATEADD(day,1,myDate) as myDate
FROM cte
WHERE DATEADD(day,1,myDate) <= #EndDate
)
SELECT myDate
FROM cte
OPTION (MAXRECURSION 0)
declare #StartDate datetime
declare #EndDate datetime
select #StartDate = '2011-01-01' , #EndDate = '2011-08-01'
select #StartDate= #StartDate-(DATEPART(DD,#StartDate)-1)
declare #temp table
(
TheDate datetime
)
while (#StartDate<=#EndDate)
begin
insert into #temp
values (#StartDate )
select #StartDate=DATEADD(MM,1,#StartDate)
end
select * from #temp
Works even if the #StartDate is not the first day of the month by going back to the initial day of the month of StartDate
this is tested in SQL 2008 R2
Declare #StartDate datetime = '2015-03-01'
Declare #EndDate datetime = '2015-03-31'
declare #temp Table
(
DayDate datetime
);
WHILE #StartDate <= #EndDate
begin
INSERT INTO #temp (DayDate) VALUES (#StartDate);
SET #StartDate = Dateadd(Day,1, #StartDate);
end ;
select * from #temp
Result:
DayDate
-----------------------
2015-03-01 00:00:00.000
2015-03-02 00:00:00.000
2015-03-03 00:00:00.000
2015-03-04 00:00:00.000
...
Interestingly, it is faster to create from enumerated data as per this article.
DECLARE #StartDate DATE = '10001201';
DECLARE #EndDate DATE = '20000101';
DECLARE #dim TABLE ([date] DATE)
INSERT #dim([date])
SELECT d
FROM
(
SELECT
d = DATEADD(DAY, rn - 1, #StartDate)
FROM
(
SELECT TOP (DATEDIFF(DAY, #StartDate, #EndDate))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM
sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
ORDER BY
s1.[object_id]
) AS x
) AS y;
On my machine, it's around 60% faster with large date ranges. The recursion method can populate 2000 years worth of data in around 3 seconds though, and looks a lot nicer, so I don't really recommend this method just for incrementing days.
Correction for null dates:
IF OBJECT_ID('tempdb..#dim') IS NOT NULL
DROP TABLE #dim
CREATE TABLE #dim ([date] DATE)
if not #Begin_Date is null and not #End_Date is null
begin
INSERT #dim([date])
SELECT d
FROM(
SELECT
d = DATEADD(DAY, rn - 1, #Begin_Date)
FROM
(
SELECT TOP (DATEDIFF(DAY, #Begin_Date, #End_Date))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM
sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
ORDER BY
s1.[object_id]
) AS x
) AS y;
end
CREATE TABLE #t (d DATE)
INSERT INTO #t SELECT GETDATE()
GO
INSERT #t SELECT DATEADD(DAY, -1, MIN(d)) FROM #t
GO 10
if i use this query thath creates a table(look Table1), But VisitingGap column is not correct format.
i used cast method to give format. But not working correctly.i need Table2
declare #date1 nvarchar(100) , #date2 nvarchar(100) , #countgap int,#count int
set #date1='2009-05-12'
set #date2 = '2009-05-13'
set #countgap = 30
set #count=48
CREATE TABLE #Temp (VisitingCount int, [Time] int, [Date] datetime )
DECLARE #DateNow DATETIME,#i int,#Time int, #Date datetime
set #DateNow='00:00'
set #i=1;
while(#i<#count)
begin
set #DateNow = DATEADD(minute, #countgap, #DateNow)
set #Time = (datepart(hour,#DateNow)*60+datepart(minute,#DateNow))/#countgap
set #Date = CONVERT(VARCHAR(5),#DateNow, 108)
insert into #Temp(VisitingCount,[Time],[Date]) values(0,#Time,#Date )
set #i=#i+1
end
select
Sum(VisitingCount) as VisitingCount, [Time],
Cast([Time]*#countgap/60 as nvarchar(50)) +':'+Cast( [Time]*#countgap%60 as nvarchar(50))
from (
select 0 as VisitingCount, [Time] from #Temp
Union All
select count(page) as VisitingCount,
(datepart(hour,Date)*60+datepart(minute,Date))/#countgap as [Time]
from scr_SecuristLog
where Date between #date1 and #date2
GROUP BY (datepart(hour,Date)*60+datepart(minute,Date))/#countgap
) X
group by [Time]
order by 2 asc
Table 1 :
.
.
.
VCount Time VisitingGap
0 1 0:30
0 2 1:0
0 3 1:30
0 4 2:0
0 5 2:30
0 6 3:0
0 7 3:30
0 8 4:0
0 9 4:30
0 10 5:0
.
.
.
Table 2 : i need below table !!!!
.
.
.
VCount Time VisitingGap
0 1 00:30
0 2 01:00
0 3 01:30
0 4 02:00
0 5 02:30
0 6 03:00
0 7 03:30
0 8 04:00
0 9 04:30
0 10 05:00
.
.
.
Look please! i think problem the cast method
Cast([Time]*#countgap/60 as nvarchar(50)) +':'+Cast( [Time]*#countgap%60 as nvarchar.........
Instead of casting numerics to strings, try converting to dates and the dates to strings:
CONVERT(CHAR(5), DATEADD(mi, [Time], '1900-01-01'), 108)
You have the answer staring you in the face in the while loop :
CONVERT(VARCHAR(5),#DateNow, 108)
Replace #DateNow with [Date] and you've got it.
What a mess. Use an auxiliary numbers table (avoids the loop) and handle your slot grouping that way. Also note that your use of between can have problems, which is why I always use a >= and < construct.
/*
-- DROP Numbers table if it exists
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Numbers]') AND type in (N'U'))
DROP TABLE [dbo].[Numbers]
GO
-- Now re-create it and fill it with sequential numbers starting at 1
SELECT TOP 10000 IDENTITY(INT,1,1) AS Num
INTO dbo.Numbers
FROM master.INFORMATION_SCHEMA.COLUMNS i1
CROSS JOIN master.INFORMATION_SCHEMA.COLUMNS i2;
GO
-- Add a primary key/clustered index to the numbers table
ALTER TABLE dbo.Numbers
ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Num);
GO
*/
CREATE TABLE #Log (
page varchar(255) NOT NULL
,date datetime NOT NULL
)
INSERT INTO #Log
VALUES (
'page1'
,'2009-05-12 08:30'
)
INSERT INTO #Log
VALUES (
'page1'
,'2009-05-12 08:35'
)
INSERT INTO #Log
VALUES (
'page2'
,'2009-05-12 07:30'
)
INSERT INTO #Log
VALUES (
'page2'
,'2009-05-12 09:35'
)
DECLARE #date1 datetime
,#date2 datetime
,#countgap int
,#count int
SET #date1 = '2009-05-12'
SET #date2 = '2009-05-13'
SET #countgap = 30
SET #count = (24 * 60) / #countgap
;
WITH LogX
AS (
SELECT *
,DATEADD(DAY, -DATEDIFF(DAY, 0, date), date) AS time
FROM #Log
WHERE date >= #date1 AND date < DATEADD(DAY, 1, #date2)
),
Slots
AS (
SELECT Num
,DATEADD(MINUTE, (Num - 1) * #countgap, 0) AS SlotStart
,DATEADD(MINUTE, Num * #countgap, 0) AS SlotEnd
FROM Numbers
WHERE Num <= #count
)
SELECT Num, CONVERT(varchar(5), SlotEnd, 108) AS [Time], COUNT(page) AS VistingCount
FROM Slots
LEFT JOIN LogX
ON LogX.TIME >= Slots.SlotStart
AND LogX.TIME < Slots.SlotEnd
GROUP BY Num, SlotEnd
DROP TABLE #Log
when you do the math, you lose the leading zeros, this will add them back when you form the string
EDIT
original code--> Cast([Time]*#countgap/60 as nvarchar(50)) +':'+ Cast( [Time]*#countgap%60 as nvarchar(50))
changed code--->RIGHT('00'+Cast([Time]*#countgap/60 as varchar(2) ),2) +':'+RIGHT('00'+Cast( [Time]*#countgap%60 as varchar(2) ),2)