Understanding TSQL Coalesce function - tsql

I am trying to select all 12 months / year. And I thought following TSQL code would do this. However, this does not include all months like I want. What is the cause of this? This is modified code:
DECLARE #END_YEAR VARCHAR(10)
DECLARE #END_MONTH VARCHAR(10)
SET #END_YEAR = '2010'
SET #END_MONTH = '10'
DECLARE #TheMonthLastDate DATETIME
DECLARE #TempDate DATETIME
SET #TempDate = '2010-11-01 00:00:00.000'
SET #TheMonthLastDate = '2010-11-01 00:00:00.000'
;with months
as
(
select dateadd(month, -1, dateadd(day, datediff(day, 0, #TempDate), 0)) as m
union all
select dateadd(month, -1, m)
from months
where m > dateadd(month, -12, #TempDate)
)
,yourTable(DateOpened, DateClosed)
as
(select TSK_START_DATE, BTK_CLOSED_DATE
FROM [PROC].ALL_AUDIT
WHERE
(BTK_CLOSED_DATE < #TheMonthLastDate OR
TSK_START_DATE < #TheMonthLastDate
)
)
select yt.DateClosed 'r2', m.m 'r3',
month(coalesce(yt.DateClosed, m.m)) as 'MonthClosed',
year(coalesce(yt.DateClosed, m.m)) as 'YearClosed'
from months m
left join yourTable yt
on
( datepart(year, yt.DateClosed) = DATEPART(year, m.m)
and datepart(month, yt.DateClosed) = DATEPART(month, m.m)
or
datepart(year, yt.DateOpened) = DATEPART(year, m.m)
and datepart(month, yt.DateOpened) = DATEPART(month, m.m)
)
AND year(coalesce(yt.DateClosed, m.m)) = 2010
order by yt.DateClosed
So above query does not return all months. But if I change above WHERE lines to:
FROM [PROC].ALL_AUDIT
WHERE
BTK_CLOSED_DATE < #TheMonthLastDate
then this query does return all 12 months. How can this be?
Output that I want and that I see when WHERE is BTK_CLOSED_DATE < #TheMonthLastDate:
r2 r3 MonthClosed YearClosed
NULL 2010-06-01 00:00:00.000 6 2010
NULL 2009-11-01 00:00:00.000 11 2009
2010-01-06 20:02:19.127 2010-01-01 00:00:00.000 1 2010
2010-01-27 23:13:45.570 2010-01-01 00:00:00.000 1 2010
2010-02-15 14:49:14.427 2010-02-01 00:00:00.000 2 2010
2010-02-15 14:49:14.427 2009-12-01 00:00:00.000 2 2010
But if I instead use WHERE:
(BTK_CLOSED_DATE < #TheMonthLastDate OR
TSK_START_DATE < #TheMonthLastDate
)
then I see:
r2 r3 MonthClosed YearClosed
NULL 2010-10-01 00:00:00.000 10 2010
NULL 2010-09-01 00:00:00.000 9 2010
NULL 2010-09-01 00:00:00.000 9 2010
NULL 2010-08-01 00:00:00.000 8 2010
NULL 2010-08-01 00:00:00.000 8 2010
...
So notice that in first result I see NULL for June 2010, which is what I want.
I think the problem has something to do with the fact that my data contains 2009-2011 data, but I only compare months and not years. How would I add in this additional logic?

The only place where you are reducing the data is with the WHERE clause you have already identified. Therefore, the reason you are not getting all the months you expect is down to the column TSK_START_DATE not being less than #TheMonthLastDate for all months.
To prove this hypothesis, run the following section of your query (I have commented out part of the where clause and removed everything under 'yourTable' cte). The results should show you what is being returned in the TSK_Start_Date column for your missing months and help you identify why the rows are missing when applying the < #TheMonthLastDate clause.
DECLARE #END_YEAR VARCHAR(10)
DECLARE #END_MONTH VARCHAR(10)
SET #END_YEAR = '2010'
SET #END_MONTH = '10'
DECLARE #TheMonthLastDate DATETIME
DECLARE #TempDate DATETIME
SET #TempDate = '2010-11-01 00:00:00.000'
SET #TheMonthLastDate = '2010-11-01 00:00:00.000'
;with months
as
(
select dateadd(month, -1, dateadd(day, datediff(day, 0, #TempDate), 0)) as m
union all
select dateadd(month, -1, m)
from months
where m > dateadd(month, -12, #TempDate)
)
,yourTable(DateOpened, DateClosed)
as
(select TSK_START_DATE, BTK_CLOSED_DATE
FROM [PROC].ALL_AUDIT
WHERE
(BTK_CLOSED_DATE < #TheMonthLastDate OR
--TSK_START_DATE < #TheMonthLastDate
)
)
select * , #TheMonthLastDate TheMonthLastDate from yourTable

Related

How can I get T-SQL to include correct data from last month

In the below query I return data for past 5 months, unfortunately I can not get it to include correct count from current month (June 2021), it ruturns 0 where as it should have returned 2 since I have 2 entries with StartDate 14-06-2021 and 17-06-2021 which should have been in this count.. How can I fix this?.
The output I get (missing 2 in month 6)
year month EmployeeStartet
2021 2 8
2021 3 0
2021 4 0
2021 5 4
2021 6 0
My Query
declare #thismonth1 as Date = DateAdd(
d,
1 - DatePart(d, CURRENT_TIMESTAMP),
CURRENT_TIMESTAMP
);
declare #lastMonth1 as Date = DateAdd(
d,
1 - DatePart(d, CURRENT_TIMESTAMP),
CURRENT_TIMESTAMP
);
declare #firstMonth1 as Date = DateAdd(m, -4, #lastMonth1);
WITH months AS (
SELECT
#firstMonth1 AS thisMonth
UNION ALL
SELECT
DateAdd(m, 1, thisMonth) AS thisMonth
FROM
months
WHERE
thisMonth < #lastMonth1
),
data AS (
SELECT
YEAR(StartDate) year,
MONTH(StartDate) month,
COUNT(StartDate) EmployeeStartet
FROM
EFP_EmploymentUser
WHERE
EmployType = 'fixed'
AND StartDate BETWEEN #firstmonth1
AND #thismonth1
GROUP BY
YEAR(StartDate),
MONTH(StartDate)
)
SELECT
YEAR(m.thisMonth) AS year,
MONTH(m.thisMonth) AS month,
ISNULL(d.EmployeeStartet, 0) AS EmployeeStartet
FROM
months m
LEFT OUTER JOIN data d ON d.year = YEAR(m.thisMonth)
AND d.month = MONTH(m.thisMonth)
ORDER BY
m.thisMonth ASC;
Try
declare #thismonth1 as Date = DateAdd(d, 1 - DatePart(d, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
declare #lastMonth1 as Date = DateAdd(d, 1 - DatePart(d, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
declare #firstMonth1 as Date = DateAdd(m, -4, #lastMonth1);
select #thismonth1, #lastMonth1, #firstMonth1;
This gives you
#thismonth1
#lastMonth1
#firstMonth1
2021-06-01
2021-06-01
2021-02-01
Correct #lastMonth1 and your query will run just fine. You can use EOMONTH for this.

Show every week of the Year even if there is no data

I have query that pulls data by week and groups it together. But i does not display weeks that doesn't have any data. I want show all weeks even if they don't have data as null maybe
Here is the query if someone can help me with this it will awesome
SELECT
DATEADD (week, datediff(week, 0, StartDate), -1) as 'WeekOf'
,DATEADD (week, datediff(week, 0, StartDate), +5) as 'to'
,DATEPART(wk, StartDate) as 'WeekNumber'
FROM [DESOutage].[dbo].[OPSInterruption]
Where StartDate > '2020-01-01' and EndDate <'2020-02-01'
Group by DATEADD (week, datediff(week, 0, StartDate), -1),DATEPART(wk, StartDate),DATEADD (week, datediff(week, 0, StartDate), +5)
***************Output***************
As you could see week 2 and 4 is missing out since there is no data being returned. I would still like to see week 2 and 4 in the output with maybe 0 as result.
WeekOf to WeekNumber
2019-12-29 00:00:00.000 2020-01-04 00:00:00.000 1
2020-01-12 00:00:00.000 2020-01-18 00:00:00.000 3
2020-01-26 00:00:00.000 2020-02-01 00:00:00.000 5
You probably need a calendar table. Here is a quick way of generating one, with an untested implementation of your code. I am assuming that the StartDate may contain a time component thus the need to coalesce the dates.
DECLARE #StartYear DATETIME = '20200101'
DECLARE #days INT = 366
;WITH
E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 1*10^1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), -- 1*10^2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), -- 1*10^4 or 10,000 rows
E8(N) AS (SELECT 1 FROM E4 a, E4 b), -- 1*10^8 or 100,000,000 rows
Tally(N) AS (SELECT TOP (#Days) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E8),
Calendar AS (
SELECT StartOfDay = DATEADD(dd,N-1,#StartYear),
EndOfDay = DATEADD(second, -1, DATEADD(dd,N ,#StartYear))
FROM Tally)
SELECT DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay) ), -1) as 'WeekOf'
, DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay)), +5) as 'to'
, DATEPART(wk, COALESCE(x.StartDate, c.StartOfDay)) as 'WeekNumber'
FROM Calendar c
INNER JOIN [DESOutage].[dbo].[OPSInterruption] x
ON x.StartDate > c.StartOfDay AND x.StartDate <= c.EndOfDay
WHERE c.StartOfDay > '2020-01-01' AND c.StartOfDay <'2020-02-01'
GROUP BY DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay)), -1),
DATEPART(wk, COALESCE(x.StartDate, c.StartOfDay)),
DATEADD (week, datediff(week, 0, COALESCE(x.StartDate, c.StartOfDay)), +5)

Find Number Of days and Months and years between 2 Dates

I am trying to Get Years, months and no. of days between 2 dates.
But when dates are like below, it gives wrong output (Month part of FromDate is greater than Todate).
declare #FromDate date='2010-10-27'
declare #Todate date='2012-03-02'
SELECT
DATEDIFF( mm, #FromDate, #ToDate) / 12 AS years
, datediff(mm,#FromDate, #ToDate) % 12 AS months
, DATEDIFF( dd, DATEADD( mm, DATEDIFF( mm, #FromDate, #ToDate), #FromDate), #ToDate) as Days
**It Shows Output as**
Years Months days
1 5 -25
It should be 4 months and 29 days. Please tell me how can I get desired Output.
Thanks in Advance
The following might solve the problem - but I think, the correct values would be 1 year, 4 month and 4 days!?
declare #start date = '2010-10-27'
declare #ende date = '2012-03-02'
SELECT DATEDIFF(mm, #start, #ende)/12 MyYears
,(DATEDIFF(mm, #start, #ende)%12)-1 MyMonths
,DATEDIFF(dd, DATEADD(mm, (DATEDIFF(mm, #start, #ende)%12)-1,DATEADD(yy, DATEDIFF(mm, #start, #ende)/12, #Start)), #Ende) MyDays
--Check of result
SELECT dateadd(dd, 4, dateadd(mm, 4, dateadd(yy, 1, #start))) x
I used this. This gives the desired result.
(#FromDate date,
#ToDate Date
)
DECLARE #Years INT, #Months INT, #Days INT
SET #Years = DATEDIFF(YEAR, #FromDate, #ToDate)
IF DATEADD(YY, #Years, #FromDate) > #ToDate
BEGIN
SET #Years = #Years - 1
END
SET #FromDate = DATEADD(YY, #Years, #FromDate)
SET #Months = DATEDIFF(MM, #FromDate, #ToDate)
IF DATEADD(MM, #Months, #FromDate) > #ToDate
BEGIN
SET #Months = #Months - 1
END
SET #FromDate = DATEADD(MM, #Months, #FromDate)
SET #Days = DATEDIFF(DD, #FromDate, #ToDate)
Select #Years as Years,#Months as Months,
#Days as Days
Maybe this is Correct
declare #FromDate date='2010-10-27'
declare #Todate date='2012-03-02'
SELECT
DATEDIFF( yy,#FromDate,#Todate) AS YEARS,
DATEDIFF( mm,#FromDate,#Todate) AS MONTHS,
DATEDIFF( dd,#FromDate,#Todate) AS DAYS

IIF Conditional StartDate EndDate int/date collision

I am trying to use the IIF statements in SS-2012
I have this which works fine
SELECT
NULLIF(IIF( a.EndDate is null
, DATEDIFF(MONTH, a.StartDate, getdate()) ,
IIF( a.EndDate is not null
, DATEDIFF(MONTH, a.StartDate, a.EndDate) , '')),'')
AS Months
,NULLIF(IIF( a.EndDate is null
, DATEDIFF(Day, a.StartDate, getdate()) ,
IIF( a.EndDate is not null
, DATEDIFF(Day, a.StartDate, a.EndDate) , '')),'')
AS DateDays
FROM
TableDates a
The problem I came across is if the start date and enddate are on the same date(I am trying to default it to 1 month and 30 or 31 days whatever month it is)
I am gettiing
int is incompatible with date
When trying this below
,NULLIF(
IIF ( (a.EndDate is null) , DATEDIFF(Day, a.StartDate, getdate()) ,
IIF ( (a.StartDate = a.EndDate)
, DATEADD(Day, DATEDIFF(Day, a.StartDate, a.EndDate), a.EndDate),
IIF ( (a.EndDate is not null) , DATEDIFF(Day, a.StartDate, a.EndDate),'')
)),'')
How can I default the days to 30-31 and month to 1 if the dates are the same?
If you need to show the number of days of the month as default where start date and enddate are on the same date then i hope the below query will work for you
IIF ( (a.StartDate = a.EndDate),
(datediff(day, a.StartDate, dateadd(month, 1, a.StartDate))),...
and to show the month as default where start date and enddate are on the same date
IIF ( (a.StartDate = a.EndDate),Month(a.StartDate),...

SQL Server not showing correct week numbers for given date

SQL Server is showing week 53 for first week of 2011 except 1th of January, and needs to be week 1.
Below is the query and output:
declare #T table (dt datetime)
insert into #T values
('2010-12-26'),
('2010-12-27'),
('2010-12-28'),
('2010-12-29'),
('2010-12-30'),
('2010-12-31'),
('2011-01-01'),
('2011-01-02'),
('2011-01-03'),
('2011-01-04'),
('2011-01-05'),
('2011-01-06'),
('2011-01-07'),
('2011-01-08')
select dt,DATEPART(wk,dt) from #T
Output:
2010-12-26 00:00:00.000 53
2010-12-27 00:00:00.000 53
2010-12-28 00:00:00.000 53
2010-12-29 00:00:00.000 53
2010-12-30 00:00:00.000 53
2010-12-31 00:00:00.000 53
2011-01-01 00:00:00.000 1
2011-01-02 00:00:00.000 2
2011-01-03 00:00:00.000 2
2011-01-04 00:00:00.000 2
2011-01-05 00:00:00.000 2
2011-01-06 00:00:00.000 2
2011-01-07 00:00:00.000 2
2011-01-08 00:00:00.000 2
I want SQL Server to show week 1 from Dec 26th - Jan 1th. Does anybody know how to accomplish this?
Thanks and regards,
Aschwin.
It was alot harder than I first expected. I am comparing the end of last year to see if it is qualified to be part of the new year. If so i set the week as 1, otherwise i just use the normal week.
declare #T table (dt datetime)
insert into #T values
('2010-12-25'),
('2010-12-26'),
('2010-12-27'),
('2010-12-28'),
('2010-12-29'),
('2010-12-30'),
('2010-12-31'),
('2011-01-01'),
('2011-01-02'),
('2011-01-03'),
('2011-01-04'),
('2011-01-05'),
('2011-01-06'),
('2011-01-07'),
('2011-01-08'),
('2011-12-31'),
('2012-01-01')
select dt,
week = case when dt + 6 - datediff(day, -1, dt) % 7 = dateadd(year, datediff(year,-1, dt), 0)
then 1 else datepart(week, dt) end from #t
Proof:
https://data.stackexchange.com/stackoverflow/q/110527/
I am not sure it holds for all years (but it looks like it) but you could solve this using a CASE statement.
SELECT dt
, CASE WHEN DATEPART(wk, dt) <> 53
THEN DATEPART(wk, dt)
ELSE 1
END
FROM #T
The new ISO_WEEK datepart doesn't apply to your requested output.
I Created 2 functions to deal with this issue
1) to get First or last day of the week
2) to get the week number or year
function 1
CREATE FUNCTION [dbo].[fn_GetDayOf]
(
#Date datetime,
--#FirstDayOfWeek int = 7,
#Mode int =1
)
/*
Mode 1: First Day Of Week
Mode 2: Last Day Of Week
*/
RETURNS datetime
WITH EXECUTE AS CALLER
BEGIN
Declare #Return datetime
--SET DATEFIRST #FirstDayOfWeek
IF #Mode = 1
BEGIN
select #Return = dateadd(day,-(datepart(weekday,#date)-1),convert(date,#date))
END
ELSE IF #Mode = 2
BEGIN
select #Return = dateadd(SECOND,-1,convert(datetime,dateadd(day,(datepart(weekday,#date)),convert(date,#date))))
END
ELSE
BEGIN
SET #Return = #Date
END
--SET DATEFIRST 7
RETURN #Return
END
Function 2
CREATE FUNCTION [dbo].[fn_GetYearWeek]
(
#Date datetime,
--#FirstDayOfWeek int = 7,
#Mode int =1
)
/*
Mode 1 = Week Number
Mode 2 = Year
*/
RETURNS INT
BEGIN
declare #Return int
IF #Mode = 1
BEGIN
select #Return = case when datepart(week,[dbo].[fn_GetDayOf] (#Date,1)) <> datepart(week,[dbo].[fn_GetDayOf] (#Date,2)) then datepart(week,[dbo].[fn_GetDayOf] (#Date,1)) else datepart(week,[dbo].[fn_GetDayOf] (#Date,2)) end
END
ELSE IF #Mode = 2
BEGIN
select #Return = case when datepart(WEEK,[dbo].[fn_GetDayOf] (#Date,1)) <> datepart(week,[dbo].[fn_GetDayOf] (#Date,2)) then datepart(YEAR,[dbo].[fn_GetDayOf] (#Date,1)) else datepart(YEAR,[dbo].[fn_GetDayOf] (#Date,2)) end
END
ELSE
BEGIN
SET #Return = -1
END
Return #Return
END
Running Example
declare #T table (dt datetime)
insert into #T values
('2010-12-25'),
('2010-12-26'),
('2010-12-27'),
('2010-12-28'),
('2010-12-29'),
('2010-12-30'),
('2010-12-31'),
('2011-01-01'),
('2011-01-02'),
('2011-01-03'),
('2011-01-04'),
('2011-01-05'),
('2011-01-06'),
('2011-01-07'),
('2011-01-08'),
('2011-12-31'),
('2012-01-01'),
('2012-01-02'),
('2012-12-31'),
('2013-01-01')
select
dt,
datepart(week,dt),
--case when datepart(week,[dbo].[fn_GetDayOf] (dt,1)) <> datepart(week,[dbo].[fn_GetDayOf] (dt,2)) then datepart(week,[dbo].[fn_GetDayOf] (dt,1)) else datepart(week,[dbo].[fn_GetDayOf] (dt,2)) end
[dbo].[fn_GetYearWeek] (dt,1),
[dbo].[fn_GetYearWeek] (dt,2)
from #T
result:
Another way to retrieve the total number of weeks in current year:
DECLARE #LASTDAY DATETIME
DECLARE #weeks INT
SET #LASTDAY = DATEADD(ms,-3,DATEADD(yy,0,DATEADD(yy,DATEDIFF(yy,0,GETDATE())+1,0)))
SELECT #weeks = CASE DATEname(dw,#LASTDAY)
WHEN 'MONDAY' THEN DATEPART(WK, DATEADD(wk,DATEDIFF(wk,7,#LASTDAY),5))
WHEN 'TUESDAY' THEN DATEPART(WK, DATEADD(wk,DATEDIFF(wk,7,#LASTDAY),5))
WHEN 'WEDNESDAY' THEN DATEPART(WK, DATEADD(wk,DATEDIFF(wk,7,#LASTDAY),5))
ELSE DATEPART(WK, #LASTDAY)
END
select #weeks