My data looks like below.
How can I find missing date range from ss table.
I want to find missing(ss date range) date range between se_startdate and se_enddate.
for example above.
Missing date ranges are
2014-07-01 to 2014-07-06
2014-07-18 to 2014-07-30.
There may be a simpler way to do this, but often when trying to find missing numbers/dates you need to create those numbers/dates then LEFT JOIN to your existing data to find what is missing. You can create the dates in question with a recursive cte:
WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt
UNION ALL
SELECT DATEADD(DAY,1,dt)
FROM cal
WHERE dt < '2014-07-30')
SELECT *
FROM cal
Then, you LEFT JOIN to your table to get a list of missing dates:
WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt
UNION ALL
SELECT DATEADD(DAY,1,dt)
FROM cal
WHERE dt < '2014-07-30')
SELECT DISTINCT cal.dt
FROM cal
LEFT JOIN YourTable a
ON cal.dt BETWEEN CAST(SS_StartDate AS DATE) AND CAST(SS_EndDate AS DATE)
WHERE a.SS_StartDate IS NULL
Then you need to find out whether or not consecutive rows belong in the same range, or if they have a gap between them, using DATEDIFF() and ROW_NUMBER():
WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt
UNION ALL
SELECT DATEADD(DAY,1,dt)
FROM cal
WHERE dt < '2014-07-30')
,dt_list AS (SELECT DISTINCT cal.dt
FROM cal
LEFT JOIN YourTable a
ON cal.dt BETWEEN CAST(SS_StartDate AS DATE) AND CAST(SS_EndDate AS DATE)
WHERE a.SS_StartDate IS NULL)
SELECT dt
,DATEDIFF(D, ROW_NUMBER() OVER(ORDER BY dt), dt) AS dt_range
FROM dt_list
Then use MIN() and MAX() to get the ranges:
WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt
UNION ALL
SELECT DATEADD(DAY,1,dt)
FROM cal
WHERE dt < '2014-07-30')
,dt_list AS (SELECT DISTINCT cal.dt
FROM cal
LEFT JOIN YourTable a
ON cal.dt BETWEEN CAST(SS_StartDate AS DATE) AND CAST(SS_EndDate AS DATE)
WHERE a.SS_StartDate IS NULL)
,dt_range AS (SELECT dt
,DATEDIFF(D, ROW_NUMBER() OVER(ORDER BY dt), dt) AS dt_range
FROM dt_list)
SELECT MIN(dt) AS BeginRange
,MAX(dt) AS EndRange
FROM dt_range
GROUP BY dt_range;
--OPTION (MAXRECURSION 0)
Demo: SQL Fiddle
Note: If the range you're checking is more than 100 days you'll need to specify the MAXRECURSION, 0 means no limit.
Note2: If your SE dates are intended to drive the complete date range, then change the cal cte from fixed dates to queries using MIN() and MAX() respectively.
A missing range must start at either se_StartDate or ss_EndDate+1. Likewise, it must end on either se_EndDate or ss_StartDate-1. Permute the candidate ranges and discard the overlappers.
The advantage of this method is that the time precision is easily adjustable to hours, or minutes or seconds without needing to enumerate every clock tick.
SQL Fiddle Demo
SELECT DISTINCT
range_start, range_end, se_StartDate, se_EndDate
FROM MyTable t1
CROSS APPLY (
SELECT se_StartDate range_start
UNION ALL
SELECT DATEADD(day,1,SS_EndDate)
) rs
CROSS APPLY (
SELECT se_EndDate range_end
UNION ALL
SELECT DATEADD(day,-1,SS_StartDate)
FROM MyTable
WHERE
se_StartDate = t1.se_StartDate AND
se_EndDate = t1.se_EndDate AND
SS_StartDate > range_start
) re
WHERE NOT EXISTS (
SELECT 1
FROM MyTable
WHERE
range_start < SS_EndDate AND
range_end > SS_StartDate
)
Related
I need help. We need to insert data between two dates in the database using C# on button click
For example, we have two calendars, Calendar1 & Calendar2.
Now from Calendar1, we choose date 01/12/2018
And from Calendar2, we choose date 30/12/2018
When we click the "Submit" button, we need to insert the data into the database
01/12/2018 to 30/12/2018
Ex.
01/12/2018
02/12/2018
03/12/2018
You can generate a table between two dates using recursive CTEs:
with dates as (
select #startdate as dte
union all
select dateadd(day, 1, dte)
from dates
where dte < #enddate
)
select *
from dates
option (maxrecursion 0);
That said, if you need to do this for a user interface, then I think you really need a calendar table, with one row per date. Then you can simply do:
select c.dte
from calendar c
where c.dte >= #startdate and c.dte <= #enddate;
I personally prefer a Tally Table to an rCTE for things like this; for larger data sets a Tally Table tends to be much faster:
DECLARE #StartDate date = '20181201',
#EndDate date = '20181231';
WITH N AS (
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
FROM N N1, N N2, N N3, N N4) --10000 rows
SELECT DATEADD(DAY, T.I, #StartDate)
FROM Tally T
WHERE DATEADD(DAY, T.I, #StartDate) <= #EndDate;
i am updating an School Application in which i want to calculate absents of any student by their ID and selected time period. i have table in Sql Server named as CHECKINOUT that saves data of students. now i want to calculate absent of any student.when someone enters in school, an entry is made in this table with following fields
[USERID]
,[CHECKTIME]
,[CHECKTYPE]
this is screenshot of data in CHECKINOUT table
now say i have id 10 and i want to calculate absent of student during last month, i am confused in query. please help me in this
was working on it, not sure if this is the easiest but it works, i've tested it.
select missDates.userid, checkinout.userid, dt from
checkinout right join
(select * from (
SELECT DATEADD(DAY, nbr - 1, #StartDate) dt
FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY c.object_id ) AS Nbr
FROM sys.columns c
) nbrs
WHERE nbr - 1 <= DATEDIFF(DAY, #StartDate, #EndDate)) alldates
cross join (select distinct userid from checkinout) t2) missDates
on missdates.userid = checkinout.userid
and convert(date,checktime) = convert(date,dt)
where checkinout.userid is null
I have a dataset where I have some daily observations. I want to group them by date and later take the average of the daily Sum per day (in SQL).
The SQL used to group by day looks like this and works.
"SELECT Date, Sum(Margin) FROM [OpenPositions] GROUP BY Date"
I have tried the following code to do the intended query in one SQL.
"SELECT Datepart(week, Date),
Avg(avgmargin)
FROM (SELECT Datepart(year, Date) Year,
Datepart(week, Date) Week,
Sum(Margin) sum
FROM [OpenPositions]
GROUP BY Datepart(year, Date),
Datepart(week, Date)"
But this does not work (not showing any data). Can anyone find the mistake in my sql query, and how to come around it?
All help appreciated.
Try this:
Select Datepart(week, Date), Avg(avgmargin) from (
SELECT Date, Sum(Margin) AS Margin FROM [OpenPositions] GROUP BY Date)
Group By Datepart(week, Date)
Edit #2
Try This:
Select TBL.week, Avg(TBL.margin) from (
SELECT Datepart(week, Date) as week ,Datepart(day, Date), Sum(Margin) AS margin
FROM [OpenPositions] GROUP BY Datepart(week, Date),Datepart(day, Date)) as TBL
Group By TBL.week
#calc16 in regard to your response bellow, why don't you do this:
Select TBL.week, Avg(TBL.margin) from
(SELECT Datepart(week, Date) as week, Date, Sum(Margin) AS margin FROM[OpenPositions]
GROUP BY Datepart(week, Date), Date) as TBL
Group By TBL.week
Edit #4
you should add the year to the group in the inner query.
Select TBL.week, , Avg(TBL.margin) from
(SELECT Datepart(week, Date) as week,Datepart(year, Date) as year, Date, Sum(Margin) AS margin FROM[OpenPositions]
GROUP BY Datepart(week, Date), Datepart(year, Date) ,Date) as TBL
Group By TBL.week, TBL.year
I have a dataset that I need to prune on a daily basis. It is populated from a process that writes records into a table periodically.
I currently have a simple query that does this:
DELETE FROM dataTable WHERE entryDate < dateadd(day, -5, GETDATE())
But the problem is that the process is unreliable; there may be days where no data is written at all.
So what I really need is a query that goes back 5 (possibly non-consecutive) days in which data is written, not 5 calendar days.
For example, if I run the following query:
SELECT cast(entryDate as date) as LogDate
FROM dataTable
group by category, cast(entryDate as date)
order by cast(entryDate as date) desc
I might get as a result:
Category Date
Foo 2015-11-30
Foo 2015-11-29
Foo 2015-11-26
Foo 2015-11-25
Foo 2015-11-21
Foo 2015-11-20 <-- Start Pruning here, not the 25th.
Foo 2015-11-19
Foo 2015-11-18
Bar 2015-11-30
Bar 2015-11-29
Bar 2015-11-28
Bar 2015-11-27
Bar 2015-11-26
Bar 2015-11-25 <-- This one is OK to prune at the 25th.
Bar 2015-11-24
Bar 2015-11-23
I need the query to go all the way back to the 20th before it deletes.
You can use row_number to get the last 5 days when the table had an entry. Then delete based on the generated numbers.
SQL Fiddle
with rownums as (SELECT row_number() over(partition by category order by cast(entryDate as date) desc) as rn
,*
FROM dataTable
)
delete from rownums where rn <= 5 --use > 5 for records prior to the last 5 days
Use dense_rank to number the rows if there can be multiple entries per day.
with rownums as (SELECT dense_rank() over(partition by category order by cast(entryDate as date) desc) as rn
,*
FROM dataTable)
delete from rownums where rn > 5;
Try maybe something like this.
;WITH orderedDates (LogDate, RowNum)
AS
(
SELECT [CACHEDATE] AS LogDate, ROW_NUMBER() OVER (ORDER BY CACHEDATE DESC) AS RowNum
FROM
dataTable
GROUP BY CACHEDATE
)
DELETE dataTable
WHERE CACHEDATE IN
(SELECT LogDate FROM orderedDates
WHERE ROWNUM > 5) --or however many you need to go back
I have a table named Leave it contains 2 rows
no=1 Leave=CL,Lnumber=2,FromDate='2015-01-10',ToDate=2015-01-11'
no=2 Leave=CL,Lnumber=2,FromDate='2015-01-12',ToDate=2015-01-13'
no=3 Leave=CL,Lnumber=2,FromDate='2015-01-15',ToDate='2015-02-16'
no=4 Leave=CL,Lnumber=2,FromDate='2015-01-31',ToDate='2015-02-01'
Here I want to get january month leave report(leave and Lnumer). How can I fetch this considering this FromDate and ToDate
The answer should be like this
Leave=CL,Lnumber=7 (for jan month)
Leave=Cl,Lnumber=1 (for feb month)
I am using MSSQL 2008. You can add extra fields if it is necessory. Thank You
I'd recommend building a calendar table (basically a tally table, but with dates). Then join your leave table to the calendar table between the from and to dates, and count the days that fall in the interval.
;with t as
(
select no=1, Leave='CL',Lnumber=2,FromDate=cast('2015-01-10' as date),ToDate=cast('2015-01-11' as date) union all
select no=2, Leave='CL',Lnumber=2,FromDate=cast('2015-01-31' as date),ToDate=cast('2015-02-01' as date)
), cal as
(
select top 10000 _date = cast(cast(row_number() over (order by (select null)) + 41000 as datetime) as date)
from sys.objects a, sys.objects b
)
select
t.Leave,
_year = datepart(year, c._date),
_month = datepart(month, c._date),
count(_date)
from t
inner join cal c
on c._date between t.FromDate and t.ToDate
group by t.Leave, datepart(year, c._date), datepart(month, c._date)