Complicated query involving res - sql

I have the table as below and I need to have the query how many tasks created for the day and how many are resolved and I need to capture the Tasks which are passed from previous day with active status. Ex: TaskId 101 is created on 11/10/2014 and it is resolved on 11/12/2014 so it should show in the count of 11/11/2014 and 11/12/2014 also.
TaskId CreateDate Status ResolvedDate
101 11/10/2014 Resolved 11/12/2014
102 11/10/2014 Resolved 11/10/2014
103 11/11/2014 Active NULL
104 11/11/2014 Resolved 11/13/2014
105 11/13/2014 Active 11/13/2014
Please help me as I am not able to think of any solution. Sorry I was trying to post the table schema in table format but not able to create table and I am new to this forum.

#radar, You just gave me the hope with the query and I have changed the query as below and it started showing result. But the TotalActive showing less value than TotalCreated.
declare #start_date datetime = getdate()-30
declare #end_date datetime = getdate()
;WITH CTE AS
(
SELECT #start_date AS date
UNION ALL
SELECT DATEADD(day, 1, date) as date
FROM CTE
WHERE DATEADD(day, 1, date) <= #end_date
)
select cte.date,
sum( case when CONVERT(varchar, CreateDate, 101) <= CONVERT(varchar, cte.date, 101) and CONVERT(varchar, cte.date, 101) <= CONVERT(varchar, ResolveDate, 101) then 1 else 0 end
) as TotalActive,
sum( case when CONVERT(varchar, cte.date, 101) = CONVERT(varchar, CreateDate, 101) then 1 else 0 end
) as TotalCreated,
sum( case when CONVERT(varchar, cte.date, 101) = CONVERT(varchar, ResolveDate, 101) then 1 else 0 end
) as TotalResolved
from cte
left join [WarehouseIncidents] T
ON CONVERT(varchar, CreateDate, 101) >= CONVERT(varchar, cte.date, 101)
GROUP BY CTE.DATE

you can get the result using case based aggregation
The CTE generates list of dates for a given range.
For each day, total active ( created that day and created previously but not resolved) and total resolved that day are calculated using case, max and group by
declare #start_date datetime = '2014-11-10'
declare #end_date datetime ='2014-11-13'
;WITH CTE AS
(
SELECT #start_date AS date
UNION ALL
SELECT DATEADD(day, 1, date) as date
FROM CTE
WHERE DATEADD(day, 1, date) <= #end_date
)
select cte.date,
sum( case when createDate <= cte.date and ( resolveddate is null or ResolvedDate >= cte.date) then 1 else 0 end
) as TotalActive,
sum( case when cte.date = ResolvedDate then 1 else 0 end
) as TotalResolved
from cte
left join Table1 T
ON T.createDate <= cte.date
GROUP BY CTE.DATE

Related

How to determine the number of days in a month for a given Date Range?

I need to calculate using SQL Query, how many days within a given range fall into each calendar month.
I have given 2 dates, which define a date range; for example 2020-01-01 to 2020-08-03. I need to find how many days in that range fall in to each month i.e. how many fall into July, and how many into August.
In the example given, the expected result is 31 days in July and 3 days in August.
One approach uses a recusive query. Using date artithmetics, we can build the query so it performs one iteration per month rather than one per day, so this should be a rather efficient approach:
with cte as (
select
datefromparts(year(#dt_start), month(#dt_start), 1) month_start,
1 - day(#dt_start) + day(
case when #dt_end > eomonth(#dt_start)
then eomonth(#dt_start)
else #dt_end
end
) as no_days
union all
select
dateadd(month, 1, month_start),
case when #dt_end > dateadd(month, 2, month_start)
then day(eomonth(dateadd(month, 1, month_start)))
else day(#dt_end)
end
from cte
where dateadd(month, 1, month_start) <= #dt_end
)
select * from cte
Demo on DB Fiddle.
If we set the boundaries as follows:
declare #dt_start date = '2020-07-10';
declare #dt_end date = '2020-09-10';
Then the query returns:
month_start | no_days
:---------- | ------:
2020-07-01 | 22
2020-08-01 | 31
2020-09-01 | 10
You can refer this
;with dates(thedate) as (
select dateadd(yy,years.number,0)+days.number
from master..spt_values years
join master..spt_values days
on days.type='p' and days.number < datepart(dy,dateadd(yy,years.number+1,0)-1)
where years.type='p' and years.number between 100 and 150
-- note: 100-150 creates dates in the year range 2000-2050
-- adjust as required
)
select dateadd(m,datediff(m, 0, d.thedate),0) themonth, count(1)
from dates d
where d.thedate between '2020-01-01' and '2020-08-03'
group by datediff(m, 0, d.thedate)
order by themonth;
Please refer the link below where RichardTheKiwi user given a clear example for your scenario.
SQL Server query for total number of days for a month between date ranges
You can do all the work at the month level rather than the day level -- which should be a bit faster. Here is a method using a recursive CTE:
with cte as (
select #startdate as startdate, #enddate as enddate,
datefromparts(year(#startdate), month(#startdate), 1) as month
union all
select startdate, enddate, dateadd(month, 1, month)
from cte
where dateadd(month, 1, month) < #enddate
)
select month,
(case when month <= startdate and dateadd(month, 1, month) >= enddate
then day(enddate) - day(startdate) + 1
when month <= startdate
then day(eomonth(month)) - day(startdate) + 1
when dateadd(month, 1, month) < enddate
then day(eomonth(month))
when dateadd(month, 1, month) >= enddate
then day(enddate)
end)
from cte;
And the db<>fiddle.
The logic is simpler at the day level:
with cte as (
select #startdate as dte, #enddate as enddate
union all
select dateadd(day, 1, dte), enddate
from cte
where dte < enddate
)
select datefromparts(year(dte), month(dte), 1) as yyyymm, count(*)
from cte
group by datefromparts(year(dte), month(dte), 1)
order by yyyymm
option (maxrecursion 0)
Here is a solution with recursive CTE.
declare #startDate date = '2020-07-01'
declare #endDate date = '2020-08-03'
; WITH cte (n, year, month, daycnt)
AS (
SELECT
0
, DATEPART(year, #startDate)
, DATENAME(MONTH, #startDate)
, DATEPART(day, EOMONTH( #startDate ) ) - DATEPART(day, #startDate ) + 1
UNION ALL
SELECT
n + 1
, DATEPART(year, DATEADD(month, n + 1, #startDate) )
, DATENAME(MONTH, DATEADD(month, n + 1, #startDate) )
, IIF(
n = ( DATEPART(month, #endDate) - DATEPART(month, #startDate) ) + ( DATEPART(year, #endDate) - DATEPART(year, #startDate) ) * 12 - 1
, DATEPART(day, #endDate )
, DATEPART(day, EOMONTH( DATEADD(month, n + 1, #startDate) ) )
)
FROM
cte
WHERE
n <= ( DATEPART(month, #endDate) - DATEPART(month, #startDate) ) + ( DATEPART(year, #endDate) - DATEPART(year, #startDate) ) * 12 - 1
)
SELECT *
FROM cte
ORDER BY n
OPTION (maxrecursion 0)
This could be further simplified with a number function but that would also be essentially be a recursive CTE, though it would definitely look cleaner. But it requires defining a function on top of this SELECT statement.

SQL Server: whole weeks total in a calendar month

I want weekly totals in a month. It will not include any partial week or future weeks. Week starts from Monday to Sunday.
I have a table structure like
Date Value -- Comments
----------------------------------------------------------------------
2016-10-01 7 Ignore this because its not a whole week in a month
2016-10-05 8 Week 1
2016-10-07 5 Week 1
2016-10-11 2 Week 2
2016-10-15 1 Week 2
2016-10-17 9 Ignore this because the week is not finished yet
OUTPUT
WeekNo Total
41 13
42 3
The easier way would be to build a Tally "date" table.
you can generate it from any Tally Table like:
DECLARE #StartDate DATE = '20160101'
, #EndDate DATE = '20161231';
WITH cte AS (
SELECT DATEADD(DAY, n - 1, #StartDate) AS date
FROM tally
WHERE n - 1 <= DATEDIFF(DAY, #StartDate, #EndDate)
)
SELECT
c.date
,YEAR(c.date) AS Year
,MONTH(c.date) AS Month
,DAY(c.date) AS Month
,DATEPART(WEEK,c.date) AS Week
,CASE WHEN 7<>COUNT(c.date) OVER (PARTITION BY YEAR(c.date),MONTH(c.date),DATEPART(WEEK,c.date)) THEN 0 ELSE 1 END AS isFullWeek
FROM cte c
Then you just need to Join it to what ever query you need.
DECLARE #StartDate datetime = '2011-10-01';
DECLARE #EndDate datetime = '2016-10-31';
SELECT
CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 2, tblData.RecordDate) AS date) AS WeekStart,
CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 8, tblData.RecordDate) AS date) AS WeekEnd,
SUM(Value) AS Total
FROM tblData
WHERE (#StartDate IS NULL
OR CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 2, tblData.RecordDate) AS date) >= CAST(#StartDate AS date))
AND (#EndDate IS NULL
OR CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 8, tblData.RecordDate) AS date) <= CAST(#EndDate AS date))
AND CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 8, tblData.RecordDate) AS date) < CAST(GETDATE() AS date)
GROUP BY CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 2, tblData.RecordDate) AS date),
CAST(DATEADD(dd, -DATEPART(dw, tblData.RecordDate) + 8, tblData.RecordDate) AS date)
Create a calendar table that meets your request, like this:
create table calendarTable ([date] date, weekNro int)
go
insert into calendarTable
select dateadd(d,n,'20160101'), DATEPART(WK,dateadd(d,n,'20151231'))
from numbers where n<500
If you don't have a Numbers Table, you must create it first. like this
SET NOCOUNT ON
CREATE TABLE Numbers (n bigint PRIMARY KEY)
GO
DECLARE #numbers table(number int);
WITH numbers(number) as (
SELECT 1 AS number
UNION all
SELECT number+1 FROM numbers WHERE number<10000
)
INSERT INTO #numbers(number)
SELECT number FROM numbers OPTION(maxrecursion 10000)
INSERT INTO Numbers(n) SELECT number FROM #numbers
Then query your table joining calendar table having in mind actual date for completed week, like this:
Similar to #Kilren but translated into postgres and using generate series from https://stackoverflow.com/a/11391987/10087503 to generate the dates
DECLARE #StartDate DATE = '20160101'
, #EndDate DATE = '20161231';
WITH cte AS (
SELECT i::date AS date FROM generate_series(#StartDate,
#EndDate, '1 day'::interval) i
)
SELECT
c.date
,DATE_TRUNC('month' ,c.date) AS month_trunc
,DATE_PART('week',c.date) AS week
,CASE WHEN 7<>COUNT(c.date)
OVER (PARTITION BY DATE_TRUNC('month' ,c.date),DATE_PART('week',c.date))
THEN 0 ELSE 1 END AS is_full_week
FROM cte c
Select DATEPART(ww, date) , SUM(Case When Comments Like '%1' then Value when Comments Like '%2' then Value else Value end)
from schema.tablename
group by DATEPART(ww,date)
I'm sorry if this doesn't work, it's the only way I thought to structure it.

SQL Count with zero values

I want to create a graph for my dataset for the last 24 hours.
I found a solution that works but this is pretty bad since the table I am outer joining cotains every single row in the DB since I am using the (now deprecated) "all" parameter in the group by.
Here is the solution that currently kind of works.
First I declare the date intervals that is 24 hours back in time from now. I declare it twice so I can use it later in the procedure aswell.
Declare #StartDate datetime = dateadd(hour, -24, getdate())
Declare #StartDateProc datetime = dateadd(hour, -24, getdate())
Declare #EndDate datetime = getdate()
I populate the dates into a temp table including a special formated datetsring.
create table #tempTable
(
Date datetime,
DateString varchar(11)
)
while #StartDate <= #EndDate
begin
insert into #tempTable (Date, DateString)
values (#StartDate, convert(varchar(8), #StartDate, 5) + '-' + convert(varchar(2), #StartDate, 108));
SET #StartDate = dateadd(hour,1, #StartDate);
end
This gives me data that looks like this:
Date DateString
---------------------------------------------
2015-12-09 13:59:01.970 09-12-15-13
2015-12-09 14:59:01.970 09-12-15-14
2015-12-09 15:59:01.970 09-12-15-15
2015-12-09 16:59:01.970 09-12-15-16
So what I want is to join my dataset on the matching date string and show the date even if the matching rows is zero.
Here is the rest of the query
select
Date = c.Date,
Amount = sum(c.Amount)
from
DbTable a
outer apply
(select
Date = b.DateString,
Amount = count(*)
from
#tempTable b
where
convert(varchar(8), a.DateColumn, 5) + '-' + convert(varchar(2), a.DateColumn, 108) = b.DateString
group by all
b.DateString) c
where
a.SomeParameter = 'test' and
a.DateColumn >= #StartDateProc and
a.DateColumn <= #EndDate
group by
c.Date
drop table #tempTable
Test to show actual data:
Declare #StartDate datetime = dateadd(hour, -24, getdate())
Declare #EndDate datetime = getdate()
select
dateString = convert(varchar(8),a.DateColumn,5) + '-' + convert(varchar(2),a.DateColumn, 108),
Amount = COUNT(*)
from
DbTable a
where
a.someParameter = 'test' and
a.DateColumn>= dateadd(hour, -24, getdate()) and
a.DateColumn<= getdate()
group by
convert(varchar(8),a.DateColumn,5) + '-' + convert(varchar(2),a.DateColumn, 108)
First output rows:
dateString Amount
09-12-15-14 1
09-12-15-15 1
09-12-15-16 1
09-12-15-17 3
09-12-15-18 1
09-12-15-22 3
09-12-15-23 2
As you can see here there is no data for the times from 19.00 to 21.00. This is how I want the data to be displayed:
dateString Amount
09-12-15-14 1
09-12-15-15 1
09-12-15-16 1
09-12-15-17 3
09-12-15-18 1
09-12-15-19 0
09-12-15-20 0
09-12-15-21 0
09-12-15-22 3
09-12-15-23 2
Normally, this would be approached with left join rather than outer apply. The logic is simple: keep all rows in the first table along with any matching information from the second. This means put the dates table first:
select tt.DateString, count(t.DateColumn) as Amount
from #tempTable tt left join
DbTable t
on convert(varchar(8), t.DateColumn, 5) + '-' + convert(varchar(2), t.DateColumn, 108) = tt.DateString and
t.SomeParameter = 'test'
where tt.Date >= #StartDateProc and
tt.Date <= #EndDate
group by tt.DateString;
In addition, your comparison for the dates seems overly complex, but if it works for you, it works.
The best bet here would be to use DATETIME type itself and not to lose the opportunity to use indexes:
Declare #d datetime = GETDATE()
;WITH cte1 AS(SELECT TOP 25 -1 + ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) h
FROM master..spt_values),
cte2 AS(SELECT DATEADD(hh, -h, #d) AS startdate,
DATEADD(hh, -h + 1, #d) AS enddate
FROM cte1)
SELECT c.startdate, c.enddate, count(*) as amount
FROM cte2 c
LEFT JOIN DbTable a ON a.DateColumn >= c.startdate AND
a.DateColumn < c.enddate AND
a.SomeParameter = 'test'
GROUP BY c.startdate, c.enddate

Calculate last days of months for given period in SQL Server

Is it possible to do in SQL: for example I have period where #s_date = '20130101' and #e_date = '20130601' and I want to select all last days of months in this period.
This is example of result:
20130131
20130228
20130331
20130430
20130531
Thanks.
The easiest option is to have a calendar table, with a last day of the month flag, so your query would simply be:
SELECT *
FROM dbo.Calendar
WHERE Date >= #StartDate
AND Date <= #EndDate
AND EndOfMonth = 1;
Assuming of course that you don't have a calendar table you can generate a list of dates on the fly:'
DECLARE #s_date DATE = '20130101',
#e_date DATE = '20130601';
SELECT Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY Object_ID) - 1, #s_date)
FROM sys.all_objects;
Then once you have your dates you can limit them to where the date is the last day of the month (where adding one day makes it the first of the month):
DECLARE #s_date DATE = '20130101',
#e_date DATE = '20130601';
WITH Dates AS
( SELECT Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY Object_ID) - 1, #s_date)
FROM sys.all_objects
)
SELECT *
FROM Dates
WHERE Date <= #e_Date
AND DATEPART(DAY, DATEADD(DAY, 1, Date)) = 1;
Example on SQL Fiddle
You can run the following query and then adjust it by using your table details:
declare #s_date as datetime= '20130101'
declare #e_date as datetime= '20131020'
SELECT DateAdd(m, number, '1990-01-31')
FROM master.dbo.spt_values
WHERE 'P' = type
AND DateAdd(m, number, #s_date) < #e_date
example for 20130101 :
select CONVERT(VARCHAR(8),
dateadd(day, -1, dateadd(month, 1,
convert(datetime, '20130101',112))), 112)
result :
20130131
Try this query
WITH sample
AS (SELECT Cast('2013-04-01' AS DATETIME) Date
UNION ALL
SELECT Dateadd(day, 1, date) dt
FROM sample
WHERE date < Cast('2013-05-05' AS DATETIME))
SELECT *
FROM sample
Fiddle
EOMONTH(#date) is the function you need.
Here is the help page https://learn.microsoft.com/en-us/sql/t-sql/functions/eomonth-transact-sql?view=sql-server-2017
This query gets the las 50 End Of Months.
The original query used as an example is from here.
https://dba.stackexchange.com/a/186829
WITH cte AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) - 1 AS [Incrementor]
FROM [master].[sys].[columns] sc1
CROSS JOIN [master].[sys].[columns] sc2
)
SELECT top 50 EOMONTH(DATEADD(Month, -1 * cte.[Incrementor], GETDATE()))
FROM cte
WHERE EOMONTH(DATEADD(Month, -1 * cte.[Incrementor], GETDATE())) < GETDATE();

SQL query to find available future dates except weekends

I have table called "detail" where i am storing start date and end date of jobs.I have one more table called "leaves" which is also have leave startdate and leave enddate fields.I need to find the nearest available dates of a user without weekends and leave dates.
DECLARE #PackagerLastAssignedDate DATETIME
SELECT #PackagerLastAssignedDate = MAX(EndDate) FROM detail WHERE userId = 1
SELECT lveStartDate,lveEndDate FROM Leaves WHERE UserId = 1 and lveStartDate > #PackagerLastAssignedDate
Thanks In advance
Berlin.M
Try this one -
DECLARE
#DateFrom DATETIME
, #DateTo DATETIME
SELECT
#DateFrom = '20130101'
, #DateTo = '20130202'
SELECT [Date]
FROM (
SELECT [Date] = DATEADD(DAY, sv.number, t.DateFrom)
FROM (
SELECT
DateFrom = #DateFrom
, diff = DATEDIFF(DAY, #DateFrom, #DateTo)
) t
JOIN [master].dbo.spt_values sv ON sv.number <= diff
WHERE sv.[type] = 'p'
) t2
WHERE DATENAME(WEEKDAY, [Date]) NOT IN ('Saturday', 'Sunday')
AND NOT EXISTS (
SELECT 1
FROM dbo.Leaves l
WHERE l.UserId = 1
AND t2.[Date] BETWEEN l.lveStartDate AND l.lveEndDate
)