How To find attendance Mispunch in sql - sql

I have a table tbl_attendance in SQL Server with data is in this format
card_no adate time
-----------------------------------------
13 2016-08-01 2016-08-01 09:30:00
13 2016-08-01 2016-08-01 11:00:00
13 2016-08-01 2016-08-01 12:00:00
13 2016-08-01 2016-08-01 15:00:00
13 2016-08-01 2016-08-01 16:00:00
13 2016-08-02 2016-08-02 09:30:00
but when I execute my query, I want to get results in this format
card_no adate time_in time_in
----------------------------------------------------------------
13 2016-08-01 2016-08-01 09:30:00 2016-08-01 11:00:00
13 2016-08-01 2016-08-01 12:00:00 2016-08-01 15:00:00
13 2016-08-01 2016-08-01 16:00:00 null
13 2016-08-02 2016-08-02 09:30:00 null
Please help as soon as possible

Try this trick
;WITH cte
AS (SELECT *,
((Row_number()OVER(partition BY [adate] ORDER BY [time])-1)%2) + 1 AS seq_no,
((Row_number()OVER(partition BY [adate] ORDER BY [time])-1)/2) + 1 rang_no
FROM Yourtable)
SELECT [card_no],
[adate],
time_in = Min(CASE WHEN seq_no = 1 THEN [time] END),
time_in = Min(CASE WHEN seq_no = 2 THEN [time] END)
FROM cte
GROUP BY [card_no],
[adate],
rang_no
Demo
Schema Setup
IF Object_id('tempdb.dbo.#Table1') is not null
DROP TABLE #Table1
CREATE TABLE #Table1
([card_no] int, [adate] date, [time] datetime);
Sample Data
INSERT INTO #Table1
([card_no], [adate], [time])
VALUES
(13, '2016-08-01 00:00:00', '2016-08-01 09:30:00'),
(13, '2016-08-01 00:00:00', '2016-08-01 11:00:00'),
(13, '2016-08-01 00:00:00', '2016-08-01 12:00:00'),
(13, '2016-08-01 00:00:00', '2016-08-01 15:00:00'),
(13, '2016-08-01 00:00:00', '2016-08-01 16:00:00'),
(13, '2016-08-02 00:00:00', '2016-08-02 09:30:00');
Query
;WITH cte
AS (SELECT *,
((Row_number()OVER(partition BY [adate] ORDER BY [time])-1)%2) + 1 AS seq_no,
((Row_number()OVER(partition BY [adate] ORDER BY [time])-1)/2) + 1 rang_no
FROM #Table1)
SELECT [card_no],
[adate],
time_in = Min(CASE WHEN seq_no = 1 THEN [time] END),
time_in = Min(CASE WHEN seq_no = 2 THEN [time] END)
FROM cte
GROUP BY [card_no],
[adate],
rang_no
Result:
card_no adate time_in time_in
------ ---------- ----------------------- -----------------------
13 2016-08-01 2016-08-01 09:30:00.000 2016-08-01 11:00:00.000
13 2016-08-01 2016-08-01 12:00:00.000 2016-08-01 15:00:00.000
13 2016-08-01 2016-08-01 16:00:00.000 NULL
13 2016-08-02 2016-08-02 09:30:00.000 NULL

Another way with OUTER APPLY (is used to get next row with time > than in current row) and ROW_NUMBER (with %2 to get only odd rows):
SELECT t.card_no,
t.adate,
t.[time],
t.time_in
FROM (
SELECT y.*,
p.[time] as time_in,
ROW_NUMBER() OVER (PARTITION BY y.card_no, y.adate ORDER BY y.[time])%2 as seq
FROM YourTable y
OUTER APPLY (
SELECT TOP 1 *
FROM YourTable
WHERE [time] > y.[time] and [adate] = y.adate
ORDER BY [time] ASC
) p
) as t
WHERE t.seq = 1
ORDER BY [time]
Output:
card_no adate time time_in
13 2016-08-01 2016-08-01 09:30:00 2016-08-01 11:00:00
13 2016-08-01 2016-08-01 12:00:00 2016-08-01 15:00:00
13 2016-08-01 2016-08-01 16:00:00 NULL
13 2016-08-02 2016-08-02 09:30:00 NULL

Related

SQL expand datefrom and dateto span of multiple years to multiple rows

I have the following input table with an id and start and enddate. I want to expand this to multiple rows when start and enddate span over multiple years, capping on the end of the year.
drop table if exists #policies;
create table #policies (
id int,
[start_date] datetime,
end_date datetime
);
insert into #policies (id, [start_date], end_date)
values
(1, '2019-01-01', '2021-12-31'),
(2, '2018-01-01', '2020-12-31'),
(3, '2011-01-01', '2013-12-31');
select * from #policies order by [id];
id start_date end_date
1 2019-01-01 00:00:00.000 2021-12-31 00:00:00.000
2 2018-01-01 00:00:00.000 2020-12-31 00:00:00.000
3 2011-01-01 00:00:00.000 2013-12-31 00:00:00.000
Expexted result:
drop table if exists #policies_expected;
create table #policies_expected (
id int,
[start_date] datetime,
end_date datetime
);
insert into #policies_expected (id, [start_date], end_date)
values
(1, '2019-01-01', '2019-12-31'),
(1, '2020-01-01', '2020-12-31'),
(1, '2021-01-01', '2021-12-31'),
(2, '2018-01-01', '2018-12-31'),
(2, '2019-01-01', '2019-12-31'),
(2, '2020-01-01', '2020-12-31'),
(3, '2011-01-01', '2011-12-31'),
(3, '2012-01-01', '2012-12-31'),
(3, '2013-01-01', '2013-12-31');
select * from #policies_expected order by [id], start_date
id start_date end_date
1 2019-01-01 00:00:00.000 2019-12-31 00:00:00.000
1 2020-01-01 00:00:00.000 2020-12-31 00:00:00.000
1 2021-01-01 00:00:00.000 2021-12-31 00:00:00.000
2 2018-01-01 00:00:00.000 2018-12-31 00:00:00.000
2 2019-01-01 00:00:00.000 2019-12-31 00:00:00.000
2 2020-01-01 00:00:00.000 2020-12-31 00:00:00.000
3 2011-01-01 00:00:00.000 2011-12-31 00:00:00.000
3 2012-01-01 00:00:00.000 2012-12-31 00:00:00.000
3 2013-01-01 00:00:00.000 2013-12-31 00:00:00.000
I tried to solve this with a recursive cte, which worked for one row:
with recursive cte(id, start_date, end_date) as (
select
id,
start_date,
dateadd(day, -1, dateadd(year, 1, start_date)) as end_date
from
#policies
where
id = 1 -- this filter is needed to make it work for one id
union all
select
id,
dateadd(year, 1, start_date) as start_date,
dateadd(day, -1, dateadd(year, 2, start_date)) as end_date
from
cte
where
start_date < dateadd(year, -1, (select max(end_date) from #policies where id=1))
)
select * from cte;
id start_date end_date
1 2019-01-01 00:00:00.000 2019-12-31 00:00:00.000
1 2020-01-01 00:00:00.000 2020-12-31 00:00:00.000
1 2021-01-01 00:00:00.000 2021-12-31 00:00:00.000
The problem here is
select max(end_date) from #policies where id=1
I am not sure how to do this per id, if I would remove the where clause in my solution, it would take the max enddate for the whole table. This needs to happen per id.
How can I solve this, I am open for other solutions as well.

T-SQL max date and min date between two date for each day

First, thanks for your time and your help!
I have two tables:
Table 1
PersId name lastName city
---------------------------------------
1 John Smith Tirana
2 Leri Nice Tirana
3 Adam fortsan Tirana
Table 2
Id PersId salesDate
--------------------------------------------
1 1 2017-01-22 08:00:40 000
2 2 2017-01-22 09:00:00 000
3 1 2017-01-22 10:00:00 000
4 1 2017-01-22 20:00:00 000
5 3 2017-01-15 09:00:00 000
6 1 2017-01-21 09:00:00 000
7 1 2017-01-21 10:00:00 000
8 1 2017-01-21 18:55:00 000
I would like to see the first recent sales between two dates according to each city for each day I want to bring it empty if I do not have a sale
SalesDate > '2017-01-17 09:00:00 000'
and SalesDate < '2017-01-23 09:00:00 000'
Table 2, id = 5 because the record is not in the specified date range
If I wanted my results to look like
Id PersId MinSalesDate MaxSalesDate City Date
-----------------------------------------------------------------------------
1 1 2017-01-22 08:00:40 000 2017-01-22 20:00:00 000 Tirana 2017-01-22
2 2 2017-01-22 09:00:00 000 null Tirana 2017-01-22
3 3 null null Tirana 2017-01-22
4 1 2017-01-21 09:00:00 000 2017-01-21 18:55:00 000 Tirana 2017-01-21
5 2 null null Tirana 2017-01-21
6 3 null null Tirana 2017-01-21
7 1 null null Tirana 2017-01-20
8 2 null null Tirana 2017-01-20
9 3 null null Tirana 2017-01-20
vb.....
It works for 1 day, but it does not work every day for 2 days
SELECT
T2.Id, T1.PersId, T2.MIN_salesDate, T2.MAX_salesDate, T1.City
FROM Table1 T1
LEFT JOIN
(
SELECT MIN(Id) as Id, PersId, MIN(salesDate) as MIN_salesDate, MAX(salesDate) as MAX_salesDate
FROM
(
SELECT
*
,ROW_NUMBER() OVER (PARTITION BY PersId ORDER BY salesDate ASC) as RNKMIN
,ROW_NUMBER() OVER (PARTITION BY PersId ORDER BY salesDate DESC) as RNKMAX
FROM Table2 T2
WHERE salesDate Between '2017-01-17 09:00:00 000' And '2017-01-23 09:00:00 000'
) temp
WHERE RNKMIN = 1 or RNKMAX = 1
GROUP BY PersId
) T2
on T1.PersId = T2.PersId
Try this
DECLARE #Table1 TABLE(
PersId INT,
name NVARCHAR(50),
city NVARCHAR(50)
)
DECLARE #Table2 TABLE(
Id INT,
PersId INT,
salesDate NVARCHAR(50)
)
INSERT INTO #Table1 VALUES(1, 'John', 'Tirana')
INSERT INTO #Table1 VALUES(2, 'Leri', 'Tirana')
INSERT INTO #Table1 VALUES(3, 'Adam', 'Tirana')
INSERT INTO #Table2 VALUES(1, 1, '2017-01-22 08:00:40')
INSERT INTO #Table2 VALUES(2, 2, '2017-01-22 09:00:00')
INSERT INTO #Table2 VALUES(3, 1, '2017-01-22 10:00:00')
INSERT INTO #Table2 VALUES(4, 1, '2017-01-22 20:00:00')
INSERT INTO #Table2 VALUES(5, 3, '2017-01-15 09:00:00')
INSERT INTO #Table2 VALUES(6, 1, '2017-01-21 09:00:00')
INSERT INTO #Table2 VALUES(7, 1, '2017-01-21 10:00:00')
INSERT INTO #Table2 VALUES(8, 1, '2017-01-21 18:55:00')
SELECT T1.PersId, GroupTable.MinSalesDate, GroupTable.MaxSalesDate, T1.city
FROM #Table1 AS T1
LEFT JOIN
(SELECT max(salesDate) as 'MaxSalesDate', CASE WHEN min(salesDate) <> max(salesDate) THEN min(salesDate) ELSE NULL END AS 'MinSalesDate', PersId
FROM #Table2
WHERE salesDate > '2017-01-17 09:00:00' and SalesDate < '2017-01-23 09:00:00'
GROUP BY DATEPART(day, salesDate) , PersId) AS GroupTable
ON T1.PersId = GroupTable.PersId
Result:
Please try this - Please let us know if you require any modification.
SELECT a.PersId, MIN(salesDate) MinSalesDate , CASE WHEN MIN(salesDate) = MAX(salesDate)
THEN NULL ELSE MAX(salesDate) END MaxSalesDate , a.city FROM Table1 a
FULL JOIN Table2 b ON a.PersId = b.PersId
AND SalesDate > '2017-01-17 09:00:00' and SalesDate < '2017-01-23 09:00:00'
WHERE a.PersId IS NOT NULL
GROUP BY a.PersId,a.city,CAST(salesDate AS DATE)
ORDER BY a.PersId
OUTPUT
PersId MinSalesDate MaxSalesDate city
----------- ----------------------- ----------------------- ----------
1 2017-01-22 08:00:40.000 2017-01-22 20:00:00.000 Tirana
2 2017-01-22 09:00:00.000 NULL Tirana
3 NULL NULL Tirana
1 2017-01-21 09:00:00.000 2017-01-21 18:55:00.000 Tirana
(4 rows affected)
I have updated your query also. Please see.
SELECT
T2.Id, T1.PersId, T2.MIN_salesDate, T2.MAX_salesDate, T1.City
FROM Table1 T1
LEFT JOIN
(
SELECT MIN(Id) as Id, PersId, MIN(salesDate) as MIN_salesDate, MAX(salesDate) as MAX_salesDate
FROM
(
SELECT
*
,ROW_NUMBER() OVER (PARTITION BY PersId ORDER BY salesDate ASC) as RNKMIN
,ROW_NUMBER() OVER (PARTITION BY PersId ORDER BY salesDate DESC) as RNKMAX
FROM Table2 T2
WHERE salesDate Between '2017-01-17 09:00:00' And '2017-01-23 09:00:00'
) temp
WHERE RNKMIN = 1 or RNKMAX = 1
GROUP BY PersId,CAST(salesDate AS DATE)
) T2
on T1.PersId = T2.PersId
OUTPUT from your query
Id PersId MIN_salesDate MAX_salesDate City
----------- ----------- ----------------------- ----------------------- ----------
6 1 2017-01-21 09:00:00.000 2017-01-21 09:00:00.000 Tirana
4 1 2017-01-22 20:00:00.000 2017-01-22 20:00:00.000 Tirana
2 2 2017-01-22 09:00:00.000 2017-01-22 09:00:00.000 Tirana
NULL 3 NULL NULL Tirana
(4 rows affected)

Finding the missing dates between two date ranges in SQL

I have a table like
ID StartDate EndDate
AAA 2017-03-17 00:00:00.000 2017-03-19 00:00:00.000
BB 2017-06-20 00:00:00.000 2017-06-25 00:00:00.000
CC 2017-05-13 00:00:00.000 2017-05-17 00:00:00.000
DD 2017-06-20 00:00:00.000 2017-05-27 00:00:00.000
EE 2017-03-01 00:00:00.000 2017-03-05 00:00:00.000
FF 2017-08-07 00:00:00.000 2017-08-11 00:00:00.000
i need the missing dates in between these ranges and the output table should be like
ID Date
AAA 2017-03-17 00:00:00.000 -- Start date for AAA
AAA 2017-03-18 00:00:00.000
AAA 2017-03-19 00:00:00.000 -- End date for AAA
BB 2017-06-20 00:00:00.000 -- start date for BB
BB 2017-06-21 00:00:00.000
BB 2017-06-22 00:00:00.000
BB 2017-06-23 00:00:00.000
BB 2017-06-24 00:00:00.000
BB 2017-06-25 00:00:00.000 -- End date for BB
You need to add loop to get dates between start and end dates , Example :
DECLARE #table table (ID Varchar(50), StartDate datetime,EndDate datetime)
insert into #table values ('AAA','2017-03-17 00:00:00.000',' 2017-03-19 00:00:00.000')
insert into #table values ('BB','2017-06-20 00:00:00.000 ',' 2017-06-25 00:00:00.000')
insert into #table values ('CC','2017-05-13 00:00:00.000 ',' 2017-05-17 00:00:00.000')
insert into #table values ('DD','2017-06-20 00:00:00.000 ',' 2017-05-27 00:00:00.000')
insert into #table values ('EE','2017-03-01 00:00:00.000 ',' 2017-03-05 00:00:00.000')
insert into #table values ('FF','2017-08-07 00:00:00.000 ',' 2017-08-11 00:00:00.000')
SELECT * FROM #table
SELECT id, StartDate FROM #table WHERE id='AAA'
union all
SELECT id, Dateadd(day,1,startdate) AS date FROM #table WHERE id='AAA' AND Dateadd(day,1,startdate)<EndDate
union all
SELECT id, EndDate FROM #table WHERE id='AAA'
union all
SELECT id, StartDate FROM #table WHERE id='BB'
union all
SELECT id, Dateadd(day,1,startdate) AS date FROM #table WHERE id='BB' AND Dateadd(day,1,startdate)<EndDate
union all
SELECT id, EndDate FROM #table WHERE id='BB'
union all
SELECT id, StartDate FROM #table WHERE id='CC'
union all
SELECT id, Dateadd(day,1,startdate) AS date FROM #table WHERE id='CC' AND Dateadd(day,1,startdate)<EndDate
union all
SELECT id, EndDate FROM #table WHERE id='CC'
union all
SELECT id, StartDate FROM #table WHERE id='DD'
union all
SELECT id, Dateadd(day,1,startdate) AS date FROM #table WHERE id='DD' AND Dateadd(day,1,startdate)<EndDate
union all
SELECT id, EndDate FROM #table WHERE id='DD'
union all
SELECT id, StartDate FROM #table WHERE id='EE'
union all
SELECT id, Dateadd(day,1,startdate) AS date FROM #table WHERE id='EE' AND Dateadd(day,1,startdate)<EndDate
union all
SELECT id, EndDate FROM #table WHERE id='EE'
union all
SELECT id, StartDate FROM #table WHERE id='FF'
union all
SELECT id, Dateadd(day,1,startdate) AS date FROM #table WHERE id='FF' AND Dateadd(day,1,startdate)<EndDate
union all
SELECT id, EndDate FROM #table WHERE id='FF'
Output:
id StartDate
AAA 2017-03-17 00:00:00.000
AAA 2017-03-18 00:00:00.000
AAA 2017-03-19 00:00:00.000
BB 2017-06-20 00:00:00.000
BB 2017-06-21 00:00:00.000
BB 2017-06-25 00:00:00.000
CC 2017-05-13 00:00:00.000
CC 2017-05-14 00:00:00.000
CC 2017-05-17 00:00:00.000
DD 2017-06-20 00:00:00.000
DD 2017-05-27 00:00:00.000
EE 2017-03-01 00:00:00.000
EE 2017-03-02 00:00:00.000
EE 2017-03-05 00:00:00.000
FF 2017-08-07 00:00:00.000
FF 2017-08-08 00:00:00.000
FF 2017-08-11 00:00:00.000
First, i created a calendar table based on your MIN and MAX date to get all the dates involved. Then i joined it to your table.
DECLARE #calendar AS TABLE ([Date] DATETIME)
DECLARE #maxDate AS DATETIME = (SELECT CASE WHEN MAX(StartDate) > MAX(EndDate)
THEN MAX(StartDate)
ELSE MAX(EndDate)
END FROM #YourTable)
DECLARE #minDate AS DATETIME = (SELECT CASE WHEN MIN(StartDate) < MIN(EndDate)
THEN MIN(StartDate)
ELSE MIN(EndDate)
END FROM #YourTable)
WHILE (#minDate < #maxDate)
BEGIN
INSERT INTO #calendar
VALUES (#minDate)
SET #minDate = DATEADD(DAY, 1, #minDate)
END
SELECT [Id], a.[Date]
FROM (Select [Date] FROM #calendar) a
LEFT JOIN #YourTable ON [Date] BETWEEN [StartDate] AND [EndDate]
WHERE [Id] IS NOT NULL
CREATE TABLE t
(
ID NVARCHAR(5) NOT NULL ,
StartDate DATETIME NOT NULL ,
EndDate DATETIME NOT NULL
);
GO
INSERT INTO dbo.t
( ID, StartDate, EndDate )
VALUES ( N'AAA', '2017-03-17 00:00:00.000', '2017-03-19 00:00:00.000' ),
( N'BB', '2017-06-20 00:00:00.000', '2017-06-25 00:00:00.000' ),
( N'CC', '2017-05-13 00:00:00.000', '2017-05-17 00:00:00.000' ),
( N'DD', '2017-06-20 00:00:00.000', '2017-05-27 00:00:00.000' ),
( N'EE', '2017-03-01 00:00:00.000', '2017-03-05 00:00:00.000' ),
( N'FF', '2017-08-07 00:00:00.000', '2017-08-11 00:00:00.000' );
WITH cte
AS ( SELECT ID ,
StartDate
FROM dbo.t
UNION ALL
SELECT cte.ID ,
DATEADD(DAY, 1, cte.StartDate)
FROM cte
INNER JOIN dbo.t ON t.ID = cte.ID
WHERE cte.StartDate < EndDate
)
SELECT cte.ID ,
cte.StartDate
FROM cte
ORDER BY cte.ID ,
cte.StartDate;
Result:
ID StartDate
----- -----------------------
AAA 2017-03-17 00:00:00.000
AAA 2017-03-18 00:00:00.000
AAA 2017-03-19 00:00:00.000
BB 2017-06-20 00:00:00.000
BB 2017-06-21 00:00:00.000
BB 2017-06-22 00:00:00.000
BB 2017-06-23 00:00:00.000
BB 2017-06-24 00:00:00.000
BB 2017-06-25 00:00:00.000
CC 2017-05-13 00:00:00.000
CC 2017-05-14 00:00:00.000
CC 2017-05-15 00:00:00.000
CC 2017-05-16 00:00:00.000
CC 2017-05-17 00:00:00.000
DD 2017-06-20 00:00:00.000
EE 2017-03-01 00:00:00.000
EE 2017-03-02 00:00:00.000
EE 2017-03-03 00:00:00.000
EE 2017-03-04 00:00:00.000
EE 2017-03-05 00:00:00.000
FF 2017-08-07 00:00:00.000
FF 2017-08-08 00:00:00.000
FF 2017-08-09 00:00:00.000
FF 2017-08-10 00:00:00.000
FF 2017-08-11 00:00:00.000

Find range of dates within same column

I have a data set, which looks like this:
ResourceID RequirementId ProjectID Startdate EndDate BillingPercentage
-------------------- -------------------- -------------------- ----------------------- ----------------------- ---------------------------------------
1 5066 7505 2015-09-15 00:00:00.000 2015-09-30 00:00:00.000 50
2 4748 7499 2015-09-10 00:00:00.000 2015-09-20 00:00:00.000 50
I want to calculate range and corresponding billing % for that particular month my query is:
INSERT INTO #DateTimeline
SELECT #MonthStartDate AS OSTARTDATE,#MonthEndDate AS OENDDATE,0
INSERT INTO #DateTimeline
SELECT Startdate AS OSTARTDATE,EndDate AS OENDDATE,BillingPercentage From #RESOURCE_UNBILLED Order by Startdate
INSERT INTO #DateTimeline
SELECT EndDate AS OSTARTDATE,EndDate AS OENDDATE,BillingPercentage From #RESOURCE_UNBILLED Order by Startdate
And data looks like following:
SerialNo OSTARTDATE OENDDATE BillingPercentage
----------- ----------------------- ----------------------- ---------------------------------------
1 2015-09-01 00:00:00.000 2015-09-30 00:00:00.000 0
2 2015-09-10 00:00:00.000 2015-09-20 00:00:00.000 50
3 2015-09-15 00:00:00.000 2015-09-30 00:00:00.000 50
4 2015-09-20 00:00:00.000 2015-09-20 00:00:00.000 50
5 2015-09-30 00:00:00.000 2015-09-30 00:00:00.000 50
I want to retrive data like following
OSTARTDATE OENDDATE BillingPercentage
----------- ----------------------- ----------------------- ---------------------------------------
2015-09-01 00:00:00.000 2015-09-10 00:00:00.000 0
2015-09-10 00:00:00.000 2015-09-15 00:00:00.000 50
2015-09-15 00:00:00.000 2015-09-20 00:00:00.000 100
2015-09-20 00:00:00.000 2015-09-30 00:00:00.000 50
Please suggest how can I get this also can I use pivot here?
Use a table variable to store your #dateStamps with columns: SerialNo, OSTARTDATE and OENDDATE.
Try this query:
SELECT d.SerialNo, d.OSTARTDATE, d.OENDDATE
, ( SELECT SUM(t.BillingPercentage)
FROM yourTable t
WHERE d.OENDDATE BETWEEN t.Startdate AND t.EndDate
OR d.OSTARTDATE BETWEEN t.Startdate AND t.EndDate
OR (d.OSTARTDATE > t.Startdate AND d.OENDDATE < t.EndDate)
) AS BillingPercentage
FROM
#dateStamps d
My complete code is:
DECLARE #DatePart as int = 5
;WITH dateStamps AS (
SELECT 1 As SerialNo, CAST('2015-' + CONVERT(varchar, MONTH(MIN(t.Startdate))) + '-01 00:00:00.000' As datetime) AS OSTARTDATE
, CAST('2015-' + CONVERT(varchar, MONTH(MIN(t.Startdate))) + '-01 00:00:00.000' As datetime) + (#DatePart - 1) AS OENDDATE
FROM yourTable t
UNION ALL
SELECT ds.SerialNo + 1, ds.OSTARTDATE + #DatePart, ds.OSTARTDATE + (#DatePart * 2 - 1)
FROM dateStamps ds
WHERE MONTH(OSTARTDATE + #DatePart) <= MONTH(ds.OSTARTDATE)
)
SELECT d.SerialNo, d.OSTARTDATE, d.OENDDATE
, ( SELECT SUM(t.BillingPercentage)
FROM t
WHERE d.OENDDATE BETWEEN t.Startdate AND t.EndDate
OR d.OSTARTDATE BETWEEN t.Startdate AND t.EndDate
OR (d.OSTARTDATE > t.Startdate AND d.OENDDATE < t.EndDate)
) AS BillingPercentage
FROM
dateStamps d

Display lines with 0 for measures if date not in date dimension table

I have difficulties to make a join between a fact table and a date dimension table because I would like to display records with a date not in the dimension table.
Example : I don't have records for 2014-09-08 at 01:00 when I try this query because there is no records in the fact table with these filters.
select *
from FCT_SCAN scan
left join dim_date dt
on cast ( scan.DATE_HEURE as date ) = dt.DATE
and cast(cast ( scan.DATE_HEURE as time(0)) as varchar(5)) = CAST(dt.heure as varchar(5))
where CAST(scan.DATE_HEURE as DATE) = '2014-08-09'
and tranche_1h = '01:00:00'
order by heure
And I would like to display records with NULL or 0 values for the fields if the DATE_HEURE field is not in the dimension table.
Edit 1:
First I rewrite my initial query with the good prefixes for a better understanding.
select *
from FCT_SCAN scan
left join dim_date dt
on cast ( scan.DATE_HEURE as date ) = dt.DATE
and cast(cast ( scan.DATE_HEURE as time(0)) as varchar(5)) = CAST(dt.heure as varchar(5))
where CAST(scan.DATE_HEURE as date) = '2014-08-09'
and dt.tranche_1h = '01:00:00'
order by dt.heure
My problem is the following : I'm searching a special conditional join which will allow me to link my fact table with my date dimension table in Cognos. And this join must allow me to display "empty" records if some datetimes in the dimension table are not in the fact table AND records in the fact table if datetimes are present.
Update : Here are CREATE TABLE and SELECT scripts of DIM_DATE.
CREATE TABLE [dbo].[DIM_DATE](
[DATE_HEURE] [datetime] NOT NULL,
[ANNEE] [int] NULL,
[MOIS] [int] NULL,
[JOUR] [int] NULL,
[DATE] [date] NULL,
[JOUR_SEM_DATE] [varchar](10) NULL,
[NUM_JOUR_SEM_DATE] [int] NULL,
[HEURE] [time](0) NULL,
[TRANCHE_1H] [time](0) NULL,
[TRANCHE_DEMIH] [time](0) NULL,
[TRANCHE_QUARTH] [time](0) NULL,
[TRANCHE_10M] [time](0) NULL,
CONSTRAINT [PK_DIM_DATE] PRIMARY KEY CLUSTERED
(
[DATE_HEURE] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
DATE_HEURE ANNEE MOIS JOUR DATE JOUR_SEM_DATE NUM_JOUR_SEM_DATE HEURE TRANCHE_1H TRANCHE_DEMIH TRANCHE_QUARTH TRANCHE_10M
2013-01-01 00:00:00.000 2013 1 1 2013-01-01 Tuesday 3 00:00:00 00:00:00 00:00:00 00:00:00 00:00:00
2013-01-01 00:01:00.000 2013 1 1 2013-01-01 Tuesday 3 00:01:00 00:00:00 00:00:00 00:00:00 00:00:00
2013-01-01 00:02:00.000 2013 1 1 2013-01-01 Tuesday 3 00:02:00 00:00:00 00:00:00 00:00:00 00:00:00
2013-01-01 00:03:00.000 2013 1 1 2013-01-01 Tuesday 3 00:03:00 00:00:00 00:00:00 00:00:00 00:00:00
2013-01-01 00:04:00.000 2013 1 1 2013-01-01 Tuesday 3 00:04:00 00:00:00 00:00:00 00:00:00 00:00:00
2013-01-01 00:05:00.000 2013 1 1 2013-01-01 Tuesday 3 00:05:00 00:00:00 00:00:00 00:00:00 00:00:00
2013-01-01 00:06:00.000 2013 1 1 2013-01-01 Tuesday 3 00:06:00 00:00:00 00:00:00 00:00:00 00:00:00
2013-01-01 00:07:00.000 2013 1 1 2013-01-01 Tuesday 3 00:07:00 00:00:00 00:00:00 00:00:00 00:00:00
2013-01-01 00:08:00.000 2013 1 1 2013-01-01 Tuesday 3 00:08:00 00:00:00 00:00:00 00:00:00 00:00:00
2013-01-01 00:09:00.000 2013 1 1 2013-01-01 Tuesday 3 00:09:00 00:00:00 00:00:00 00:00:00 00:00:00
There is 1 record by minute from 2013-01-01 00:00:00 to 2017-12-31 23:59:00 stored in this table.
We make the join between a DATE_HEURE field in FCT_SCAN and the fields of DIM_DATE.
Here is the DATE_HEURE field in FCT_SCAN :
DATE_HEURE
2014-10-17 21:39:27.000
2014-10-17 21:44:37.000
2014-10-17 23:14:05.000
2014-10-17 23:14:01.000
2014-10-17 21:40:09.000
2014-10-17 21:44:25.000
2014-10-17 21:41:41.000
2014-10-17 21:41:51.000
2014-10-17 21:48:12.000
2014-10-17 23:09:32.000
I don't show you all the fields of FCT_SCAN because there is about 180 fields so...
Edit 2:
For information, my desired output looks like this if there is no data between 01:00 and 01:30 :
DATE_HEURE FIELD0 FIELD1 FIELD2 MEASURE0 MEASURE1 MEASURE2
2015-02-03 00:00:00 XXX XXX XXX 5 42 23
2015-02-03 00:30:00 XXX XXX XXX 5 42 23
2015-02-03 01:00:00 NULL NULL NULL 0 0 0
2015-02-03 01:30:00 NULL NULL NULL 0 0 0
2015-02-03 02:00:00 XXX XXX XXX 5 42 23
2015-02-03 02:30:00 XXX XXX XXX 5 42 23
Try Outer Apply:
select *
from FCT_SCAN scan
OUTER APPLY( select * from dim_date dt
where cast ( scan.DATE_HEURE as date ) = dt.DATE
and cast(cast ( scan.DATE_HEURE as time(0)) as varchar(5)) = CAST(dt.heure as varchar(5))) o
where CAST(scan.DATE_HEURE as DATE) = '2014-08-09'
and tranche_1h = '01:00:00'
order by heure
If tranche_1h column is from dim_date then use:
select *
from FCT_SCAN scan
OUTER APPLY( select * from dim_date dt
where cast ( scan.DATE_HEURE as date ) = dt.DATE
and cast(cast ( scan.DATE_HEURE as time(0)) as varchar(5)) = CAST(dt.heure as varchar(5)) and tranche_1h = '01:00:00') o
where CAST(scan.DATE_HEURE as DATE) = '2014-08-09'
order by heure
You may just be having issues as you need the time to the minute rather than second?
select *
from fct_scan scan
RIGHT JOIN dim_date dt
on dt.Date = cast ( scan.DATE_HEURE as date )
and cast(cast(cast(scan.DATE_HEURE as time(0)) as varchar(5)) as time) = dt.heure
order by heure