Related
I've table in my schema => TimeSlot, which has several attributes with id, start time, end time.
So the question is: how can you make a script that will make 23/24 records, for which starttime - endtime, for example, 0.00-1.00, 1.00-2.00, etc.
i've tried this with some example, but can not figure out how to code it(not even know if it is correct):
SET NOCOUNT ON
TRUNCATE TABLE TimeSlot
DECLARE #InitHour TIME(0) = '1'
DECLARE #FinitHour TIME(0) = '24'
WHILE #InitHour < #FinitHour
BEGIN
INSERT INTO TimeSlot(
TimeSlotID, StartTime, EndTime
)
SELECT
The simplest solution is to generate a sequence of numbers, you can do this using any table that has the required number of rows and use row_number for an arbitrary number sequence.
with seq as (
select top(24) Row_Number() over (order by (select null))-1 n
from sys.syscomments
)
insert into TimeSlot (StartTime, EndTime)
select Convert(time,Concat(n,':00')) StartTime,
DateAdd(hour, 1, Convert(time,Concat(n,':00'))) EndTime
from seq
The basic answer to your question is that you need to generate the results somehow. There are lots of methods. If you have a table lying around with at least 24 rows, then you can use that. A "native" SQL method uses a recursive CTE>
Then, the TimeSlotID should be generated automatically (typically as an identity column):
WITH CTE as (
SELECT convert(datetime, '01:00:00') as StartTime
UNION ALL
SELECT DATEADD(HOUR, 1, StartTime)
FROM CTE
WHERE StartTime < convert(datetime, '23:00:00')
)
INSERT INTO TimeSlot (StartTime, EndTime)
SELECT StartTime, DATEADD(HOUR, 1, StartTime)
FROM CTE;
Fiddle here: http://sqlfiddle.com/#!18/9bac2/12
Please, check this solution: http://sqlfiddle.com/#!18/4cb97/17
set nocount on
declare #hour int = 0
while #hour <= 23
begin
declare #time datetime =
convert(
datetime,
convert(varchar, #hour) + ':00:00'
)
insert into TimeSlot(StartTime, EndTime)
values(
#time, dateadd(hour, 1, #time)
)
set #hour = #hour + 1
end
select *
from TimeSlot
Two Column in table tblpress
Date Time
20160307 120949
20160307 133427
Need to be select below the format:
07-03-2016 12:09:49
07-03-2016 13:34 27
or
03-March-2016 12:09: 49 PM
03-March-2016 01:34: 27 PM
You can try below
select format(cast([Date] as date),'dd-MMMM-yyyy') as [Date],
TIMEFROMPARTS(LEFT([Time],2), SUBSTRING([Time],3,2), RIGHT([Time],2), 0,0) as [Time]
I think CAST/CONVERT will help you:
SELECT
CAST('20160307' AS date),
CAST(STUFF(STUFF('120949',3,0,':'),6,0,':') AS time)
And convert for out:
SELECT
CONVERT(varchar(20),NormalDate,105) OutDate, -- Italian style
CONVERT(varchar(20),NormalTime,108) OutTime -- hh:mi:ss
FROM
(
SELECT
CAST([Date] AS date) NormalDate,
CAST(STUFF(STUFF([Time],3,0,':'),6,0,':') AS time) NormalTime
FROM YourTable
) q
CAST and CONVERT (Transact-SQL)
And you can use FORMAT (Transact-SQL)
SELECT
FORMAT(GETDATE(),'dd-MM-yyyy'),
FORMAT(GETDATE(),'HH:mm:ss')
Best way to do it is to create a function :
create FUNCTION [dbo].[udfGetDateTimeFromInteger]
(
#intDate int,
#intTime int
)
RETURNS datetime
AS BEGIN
-- Declare the return variable here
DECLARE #DT_datetime datetime = NULL,
#str_date varchar(11),
#str_time varchar(8)
if(#intDate is not null and #intDate > 0)
begin
select #str_date = CAST( cast(#intDate as varchar(8)) AS date)
if #intTime=0
select #str_time ='000000'
else
select #str_time = right('0'+CONVERT(varchar(11),#intTime),6)
select #str_time =
SUBSTRING(#str_time,1,2)+':'+SUBSTRING(#str_time,3,2)+':'+SUBSTRING(#str_time,5,2)
select #DT_datetime = CAST(#str_date+' '+#str_time as datetime)
end
-- Return the result of the function
RETURN #DT_datetime
END
and then call it in select like :
declare #next_run_date int, #next_run_time int
select #next_run_date = 20160307
select #next_run_time = 130949
SELECT #next_run_date inputdate,
#next_run_time inputtime,
dbo.udfGetDateTimeFromInteger(#next_run_date, #next_run_time) outputdatetime
Output will be like :
inputdate inputtime outputdatetime
20160307 130949 2016-03-07 13:09:49.000
You said those are numbers, right? You can use datetimefromparts (or datetime2fromparts). ie:
select
datetimefromparts(
[date]/10000,
[date]%10000/100,
[date]%100,
[time]/10000,
[time]%10000/100,
[time]%100,0)
from tblpress;
DB Fiddle demo
Note that naming fields like that and also storing date and time like that is a bad idea.
I later noticed it was char fields:
select
cast([date] as datetime) +
cast(stuff(stuff([time],5,0,':'),3,0,':') as datetime)
from tblpress;
I have a data set that I need to filter a date that is stored as a string (changing the source column to a DateTime is NOT a option, this data is coming from a 3rd party source that I can not control).
One of the dates is malformed so if I do the following query I get one result
select ClientID, StartDate from boarding_appts where isdate(StartDate) = 0
ClientID StartDate
---------- --------------------
5160 5/6/210 12:00:00
If I do a cast(StartDate as datetime) I get "Arithmetic overflow error converting expression to data type datetime.", which I expected. and if I filter by IsDate alone everything works fine
select ClientID, cast(StartDate as dateTime) as StartDateCast, datediff(year, cast(StartDate as dateTime), getdate()) as age from boarding_appts where isdate(StartDate) = 1
ClientID StartDate age
---------- ----------------------- ----------
10207 2012-06-09 12:00:00.000 1
2843 2012-06-23 12:00:00.000 1
2843 2012-06-23 12:00:00.000 1
8292 2012-05-11 12:00:00.000 1
7935 2012-04-24 12:00:00.000 1
... (1000's of more rows) ...
Here is my problem:
I want to filter out records so only records a year old or newer show up, however no-matter how I attempt to perform the filter every one of these queries give me an arithmetic overflow error.
select ClientID, cast(StartDate as dateTime) as StartDateCast, datediff(year, cast(StartDate as dateTime), getdate()) as age
from boarding_appts
where isdate(StartDate) = 1
and datediff(year, cast(StartDate as dateTime), getdate()) < 1 --If you comment out this line it works fine
select *
from (select ClientID, cast(StartDate as dateTime) as StartDateCast, datediff(year, cast(StartDate as dateTime), getdate()) as age from boarding_appts where isdate(StartDate) = 1) as Filtered
where age < 1 --If you comment out this line it works fine
select *
from (select ClientID, cast(StartDate as dateTime) as StartDateCast from boarding_appts where isdate(StartDate) = 1) as Filtered
where datediff(year, StartDateCast, getdate()) < 1 --If you comment out this line it works fine
;with Filtered as
(select ClientID, cast(StartDate as dateTime) as StartDateCast from boarding_appts where isdate(StartDate) = 1)
select * from Filtered
where datediff(year, StartDateCast, getdate()) < 1 --If you comment out this line it works fine
;with Filtered as
(select ClientID, cast(StartDate as dateTime) as StartDateCast, datediff(year, cast(StartDate as dateTime), getdate()) as age from boarding_appts where isdate(StartDate) = 1)
select * from Filtered
where age < 1 --If you comment out this line it works fine
Here is a test set of data on SQL Fiddle for you to try out any solutions on. I am out of ideas on how to fix this. The ONLY solution I could think of that worked was selecting in to a temporary table first then selecting it out
select ClientID, StartDate, cast(StartDate as dateTime) as StartDateCast, datediff(year, cast(StartDate as dateTime), getdate()) as age
into #t
from boarding_appts
where isdate(StartDate) = 1
select * from #t where age < 1 --Works.
SQL is a declarative language. The SQL optimizer is free to rearrange parts of the where clause as long as it retains its original meaning. So it can run datediff before isdate even if you specify isdate first. A subquery or CTE provides no sure relief, since that too can be rewritten.
The second suggestion from Aaron Bertrand in the comments:
WHERE CASE ISDATE(StartDate)
WHEN 1 THEN StartDate
ELSE '19000101'
END >= DATEADD(YEAR, -1, GETDATE());
Makes it unlikely that SQL Server will cast StartDate to a datetime when ISDATE = 0. That seems like the best solution.
I've marked this answer community wiki, if Aaran Bertrand posts an answer, accept that :)
SQL Server's DateTime has the domain 1753-01-01 00:00:00.000 ≤ x ≤ 9999-12-31 23:59:59.997. The year 210 CE is outside that domain. Hence the problem.
If you were using SQL Server 2008 or later, you could cast it to a DateTime2 datatype and you'd be golden (its domain is 0001-01-01 00:00:00.0000000 &le x ≤ 9999-12-31 23:59:59.9999999. But with SQL Server 2005, you're pretty much SOL.
This is really a problem of data cleaning. My inclination in cases like this is to load the 3rd party data into a staging table with each field as character strings. Then cleanse the data in place, replacing, for instance, invalid dates with NULL. Once cleansed, then do the necessary conversion work to move it to its final destination.
Another approach is to use pattern matching and do the date filtering without converting anything to datetime. ISO 8601 date/time values are character strings that have the laudable property of being (A) human-readable and (B) collating and comparing properly.
What I've done in the past is some analytical work to identify all the patterns in the datetime field by replacing decimal digits with a 'd' and then running group by to compute the counts of each different pattern found. Once you have that you can create some pattern tables to guide you. Something like these:
create table #datePattern
(
pattern varchar(64) not null primary key clustered ,
monPos int not null ,
monLen int not null ,
dayPos int not null ,
dayLen int not null ,
yearPos int not null ,
yearLen int not null ,
)
insert #datePattern values ( '[0-9]/[0-9]/[0-9] %' ,1,1,3,1,5,1)
insert #datePattern values ( '[0-9]/[0-9]/[0-9][0-9] %' ,1,1,3,1,5,2)
insert #datePattern values ( '[0-9]/[0-9]/[0-9][0-9][0-9] %' ,1,1,3,1,5,3)
insert #datePattern values ( '[0-9]/[0-9]/[0-9][0-9][0-9][0-9] %' ,1,1,3,1,5,4)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9] %' ,1,1,3,2,6,1)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9][0-9] %' ,1,1,3,2,6,2)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9][0-9][0-9] %' ,1,1,3,2,6,3)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9] %' ,1,1,3,2,6,4)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9] %' ,1,2,4,1,6,1)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9][0-9] %' ,1,2,4,1,6,2)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9][0-9][0-9] %' ,1,2,4,1,6,3)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9][0-9][0-9][0-9] %' ,1,2,4,1,6,4)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9] %' ,1,2,4,2,7,1)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9][0-9] %' ,1,2,4,2,7,2)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9] %' ,1,2,4,2,7,3)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9] %' ,1,2,4,2,7,4)
create table #timePattern
(
pattern varchar(64) not null primary key clustered ,
hhPos int not null ,
hhLen int not null ,
mmPos int not null ,
mmLen int not null ,
ssPos int not null ,
ssLen int not null ,
)
insert #timePattern values ( '[0-9]:[0-9]:[0-9]' ,1,1,3,1,5,1 )
insert #timePattern values ( '[0-9]:[0-9]:[0-9][0-9]' ,1,1,3,1,5,2 )
insert #timePattern values ( '[0-9]:[0-9][0-9]:[0-9]' ,1,1,3,2,6,1 )
insert #timePattern values ( '[0-9]:[0-9][0-9]:[0-9][0-9]' ,1,1,3,2,6,2 )
insert #timePattern values ( '[0-9][0-9]:[0-9]:[0-9]' ,1,2,4,1,6,1 )
insert #timePattern values ( '[0-9][0-9]:[0-9]:[0-9][0-9]' ,1,2,4,1,6,2 )
insert #timePattern values ( '[0-9][0-9]:[0-9][0-9]:[0-9]' ,1,2,4,2,7,1 )
insert #timePattern values ( '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' ,1,2,4,2,7,2 )
You could combine these two tables into 1 but the number of combinations tends to explode things, though it greatly simplifies the query then.
Once you have that, the query is [fairly] easy, given that SQL is not exactly the world's best language choice for string processing:
---------------------------------------------------------------------
-- first, get your lower bound in ISO 8601 format yyyy-mm-dd hh:mm:ss
-- This will compare/collate properly
---------------------------------------------------------------------
declare #dtLowerBound varchar(255)
set #dtLowerBound = convert(varchar,dateadd(year,-1,current_timestamp),121)
-----------------------------------------------------------------
-- select rows with a start date more recent than the lower bound
-----------------------------------------------------------------
select isoDate = + right( '0000' + substring( t.startDate , coalesce(dt.yearPos,1) , coalesce(dt.YearLen,0) ) , 4 )
+ '-' + right( '00' + substring( t.startDate , coalesce(dt.monPos,1) , coalesce(dt.MonLen,0) ) , 2 )
+ '-' + right( '00' + substring( t.startDate , coalesce(dt.dayPos,1) , coalesce(dt.dayLen,0) ) , 2 )
+ case
when tm.pattern is not null then
' ' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.hhPos , tm.hhLen ) , 2 )
+ ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.mmPos , tm.mmLen ) , 2 )
+ ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.ssPos , tm.ssLen ) , 2 )
else ''
end
,*
from someTableWithBadData t
left join #datePattern dt on t.startDate like dt.pattern
left join #timePattern tm on ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) )
like tm.pattern
where #lowBound <= + right( '0000' + substring( t.startDate , coalesce(dt.yearPos,1) , coalesce(dt.YearLen,0) ) , 4 )
+ '-' + right( '00' + substring( t.startDate , coalesce(dt.monPos,1) , coalesce(dt.MonLen,0) ) , 2 )
+ '-' + right( '00' + substring( t.startDate , coalesce(dt.dayPos,1) , coalesce(dt.dayLen,0) ) , 2 )
+ case
when tm.pattern is not null then
' ' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.hhPos , tm.hhLen ) , 2 )
+ ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.mmPos , tm.mmLen ) , 2 )
+ ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.ssPos , tm.ssLen ) , 2 )
else ''
end
Like I said, SQL not the best choice for munging strings.
This should get you ... 90% there. Experience tells me that you'll still find more bad dates: months less than 1 or greater than 12 , days less than 1 or greater than 31, or days out of range for that month (nothing like February 31st to make the computer whine), etc. Old cobol programs in particular, loved to use a field of all 9s to indicate missing data, for instance (though that is an easy case to deal with).
My preferred technique is to write a perl script to scrub the data and bulk load it into SQL Server, using perl's BCP facilities. That's exactly the sort of problem space perl is designed for.
If I am given a date like 1999-07-08 15:49:00 what would be a good function to determine whether is an AM shift, PM shift or a NOC shift?
--AM: 06:45:00 - 14:44:59
--PM: 14:45:00 - 22:59:59
--NOC: 23:00:00 - 06:44:59
Here is my attempt but then I noticed a bug
ALTER FUNCTION [dbo].[DateToNocShift]
(
-- Add the parameters for the function here
#DummyDate DATETIME
)
RETURNS VARCHAR(10)
AS
BEGIN
-- Declare the return variable here
DECLARE #Shift VARCHAR(10)
DECLARE #DateValues TABLE
(
RawDate DATETIME,
HourNow int,
MinuteNow int,
TimeHourMinute FLOAT,
Shift VARCHAR(4)
)
INSERT INTO #DateValues
VALUES
(
#DummyDate,
DATEPART(hour,#DummyDate),
cast(DATEPART(minute,#DummyDate)as decimal),
ROUND(DATEPART(hour,#DummyDate) + cast(DATEPART(minute,#DummyDate)as decimal)/60,2),
null
)
UPDATE #DateValues
SET Shift = 'AM'
WHERE TimeHourMinute BETWEEN 6.75 AND 14.74 -- good estimate
UPDATE #DateValues
SET Shift = 'PM'
WHERE TimeHourMinute BETWEEN 14.75 AND 22.99
UPDATE #DateValues
SET Shift = 'NOC'
WHERE TimeHourMinute BETWEEN 23.00 AND 6.74
SELECT #Shift = Shift FROM #DateValues
RETURN #Shift
You don't need so much code, a single CASE statement will do
CREATE FUNCTION [dbo].[DateToNocShift]
(
-- Add the parameters for the function here
#DummyDate DATETIME
)
RETURNS VARCHAR(10)
AS
BEGIN
DECLARE #dateChar varchar(8)
set #dateChar = convert(varchar, #DummyDate, 108)
RETURN CASE
WHEN #dateChar >= '06:45:00' and #dateChar < '14:45:00' then 'AM'
WHEN #dateChar >= '14:45:00' and #dateChar < '23:00:00' then 'PM'
ELSE 'NOC'
END -- CASE
END
Another approach would be to create a table with a row in it for every minute of the day, 1440 rows.
CREATE TABLE ShiftCheck
(
id int,
MyTime datetime not null,
ShiftNumber int not null,
ShiftName char(3) not null
CONSTRAINT [PK_ShiftCheck] PRIMARY KEY CLUSTERED
(
[id] ASC
)
)
populate that table with a statement similar to this
; WITH DateIntervalsCTE AS
(
SELECT 0 i, cast('1/1/1900' as datetime) AS Date
UNION ALL
SELECT i + 1, DATEADD(MINUTE, i,cast('1/1/1900' as datetime) )
FROM DateIntervalsCTE
WHERE DATEADD(MINUTE, i, cast('1/1/1900' as datetime) ) < cast('1/2/1900' as datetime)
)
SELECT
ROW_NUMBER() over (order by Date),
Date,
CASE
WHEN convert(varchar, Date, 108) >= '06:45:00' and convert(varchar, Date, 108) < '14:45:00' then 1
WHEN convert(varchar, Date, 108) >= '14:45:00' and convert(varchar, Date, 108) < '23:00:00' then 2
ELSE 3
END,
CASE
WHEN convert(varchar, Date, 108) >= '06:45:00' and convert(varchar, Date, 108) < '14:45:00' then 'AM'
WHEN convert(varchar, Date, 108) >= '14:45:00' and convert(varchar, Date, 108) < '23:00:00' then 'PM'
ELSE 'NOC'
END
FROM DateIntervalsCTE
OPTION (MAXRECURSION 32767);
Then, you just need to join to the ShiftCheck table on a time. There is only a need to calculate what time a shift is in one time, to populate the table.
Scalar Valued Functions, like the one listed in your question are executed for every row in a given query. For example
Select *,[dbo].[DateToNocShift](ShiftDate)
from myTable
the function will be executed for every row in myTable which at a certain point (Saturday morning while you are sleeping) will get very slow. In conclusion, this will eventually become a performance problem and eventually someone will want to see the word 1st, 2nd, 3rd for the shift name. This solution will solve both of those as well as force a look-up instead of a calculation.
*If that is not accurate enough, put a row for every second of the day (24*60*60 = 86400 rows, still not that big for sql server)
*I took the generate sql from here
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)