Failure creating a function same as stored procedure - sql-server

This is my stored procedure which I was able to create
CREATE PROCEDURE [dbo].[spGetAllStaffCollectiveAttendanceByMonth]
#Month nvarchar(9)
AS
BEGIN
Declare #StartDate DATE,
#EndDate DATE
;WITH CteMonths(n, m) AS(
SELECT 1, 'January' UNION ALL
SELECT 2, 'February' UNION ALL
SELECT 3, 'March' UNION ALL
SELECT 4, 'April' UNION ALL
SELECT 5, 'May' UNION ALL
SELECT 6, 'June' UNION ALL
SELECT 7, 'July' UNION ALL
SELECT 8, 'August' UNION ALL
SELECT 9, 'September' UNION ALL
SELECT 10, 'October' UNION ALL
SELECT 11, 'November' UNION ALL
SELECT 12, 'December'
)
SELECT #StartDate = DATEADD(MONTH, n - 1, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)),
#EndDate = DATEADD(DAY, -1, DATEADD(MONTH, n, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)))
FROM CteMonths
WHERE m = #month
SELECT
StaffAttendance.StaffId,
DATENAME(MONTH, #StartDate) AS [ForMonth], #StartDate AS StartDate, #EndDate AS EndDate,
(DATEDIFF(dd, #StartDate, #EndDate) + 1)-(DATEDIFF(wk, #StartDate, #EndDate) * 1)-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END) AS TotalWorkingDays,
SUM(StaffAttendance.AttendanceStatusId) AS TotalDaysWorked
FROM StaffAttendance
WHERE [Date] BETWEEN #StartDate AND #EndDate AND StaffAttendance.AttendanceStatusId = 1 GROUP BY StaffAttendance.StaffId
END
GO
This, here, is my function which I am trying to create but can't. Its the same thing as above except for what makes it a function.
CREATE FUNCTION [dbo].[funcGetAllStaffCollectiveAttendanceByMonth]
(
#Month nvarchar(9)
)
RETURNS TABLE
AS
RETURN
(
Declare #StartDate DATE,
#EndDate DATE
;WITH CteMonths(n, m) AS(
SELECT 1, 'January' UNION ALL
SELECT 2, 'February' UNION ALL
SELECT 3, 'March' UNION ALL
SELECT 4, 'April' UNION ALL
SELECT 5, 'May' UNION ALL
SELECT 6, 'June' UNION ALL
SELECT 7, 'July' UNION ALL
SELECT 8, 'August' UNION ALL
SELECT 9, 'September' UNION ALL
SELECT 10, 'October' UNION ALL
SELECT 11, 'November' UNION ALL
SELECT 12, 'December'
)
SELECT #StartDate = DATEADD(MONTH, n - 1, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)),
#EndDate = DATEADD(DAY, -1, DATEADD(MONTH, n, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)))
FROM CteMonths
WHERE m = #month
SELECT
StaffAttendance.StaffId,
DATENAME(MONTH, #StartDate) AS [ForMonth], #StartDate AS StartDate, #EndDate AS EndDate,
(DATEDIFF(dd, #StartDate, #EndDate) + 1)-(DATEDIFF(wk, #StartDate, #EndDate) * 1)-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END) AS TotalWorkingDays,
SUM(StaffAttendance.AttendanceStatusId) AS TotalDaysWorked
FROM StaffAttendance
WHERE [Date] BETWEEN #StartDate AND #EndDate AND StaffAttendance.AttendanceStatusId = 1 GROUP BY StaffAttendance.StaffId
END
)
But this is what I am getting
What am I doing wrong?

You have used the syntax for an inline table-valued function but you have multiple statements.
Comparison of inline and multi-statement table-valued functions.
So you could either refactor to use a single statement (higher performance) or use the multi-statement syntax as described in that link (easier).
If you wanted to do it inline you could do something along these lines (note, I don't have SSMS available right now so there may be some minor syntax errors like unmatched brackets):
CREATE FUNCTION [dbo].[funcGetAllStaffCollectiveAttendanceByMonth]
(
#Month nvarchar(9)
)
RETURNS TABLE
AS
RETURN
(
-- The StartDate should be the first day of the month that is passed as a parameter, in the current year.
-- The EndDate should be the last day of the month which the StartDate begins.
WITH cteDates AS (
SELECT StartDate = Convert(date, Concat('01', #Month, Convert(varchar(4), DatePart(Year, GetDate()))), 106),
EndDate = DateAdd(Day, -1,
DateAdd(Month, 1,
Convert(date, Concat('01', #Month, Convert(varchar(4), DatePart(Year, GetDate()))) , 106)
)
)
)
SELECT
Sa.StaffId,
DATENAME(MONTH, cteDates.StartDate) AS [ForMonth],
cteDates.StartDate,
cteDates.EndDate,
(DATEDIFF(dd, cteDates.StartDate, cteDates.EndDate) + 1)-
(DATEDIFF(wk, cteDates.StartDate, cteDates.EndDate) * 1)-
(CASE WHEN DATENAME(dw, cteDates.StartDate) = 'Sunday' THEN 1 ELSE 0 END) AS TotalWorkingDays,
SUM(sa.AttendanceStatusId) AS TotalDaysWorked
FROM StaffAttendance As sa
JOIN cteDates ON sa.[Date] BETWEEN cteDates.StartDate AND cteDates.EndDate
WHERE sa.AttendanceStatusId = 1
GROUP BY
Sa.StaffId,
DATENAME(MONTH, cteDates.StartDate),
cteDates.StartDate,
cteDates.EndDate,
(DATEDIFF(dd, cteDates.StartDate, cteDates.EndDate) + 1)-
(DATEDIFF(wk, cteDates.StartDate, cteDates.EndDate) * 1)-
(CASE WHEN DATENAME(dw, cteDates.StartDate) = 'Sunday' THEN 1 ELSE 0 END)
END
)

Related

How to get data of current date using sum in SQL?

I'm working on a SQL query trying to fetch sum data for the current day/date. Can anyone have a look at my query, and find me a working solution?
SELECT SUM(amount)
FROM tbl_expense_record
WHERE dateonly = CAST(GETDATE() AS Date)
But I get data when mentioning a specific date in where condition like
SELECT SUM(amount) AS total
FROM tbl_expense_record
WHERE dateonly = '2020-06-12'
I want the a code to auto pick current date. Also I would like to fetch sum of ranged dates like a whole week, and a month!
select datename(month, '2020-06-12'), datename(month, getdate());
--1week
SELECT SUM(amount) AS total
FROM tbl_expense_record
WHERE dateonly >= dateadd(week, -1, cast(getdate() as date))
and dateonly <= cast(getdate() as date)
--1month
SELECT SUM(amount) AS total
FROM tbl_expense_record
WHERE dateonly >= dateadd(month, -1, cast(getdate() as date))
and dateonly <= cast(getdate() as date)
--build muscle memory (it is always safe to check for < date+1 instead of <= date)
--1month
SELECT SUM(amount) AS total
FROM tbl_expense_record
WHERE dateonly >= dateadd(month, -1, cast(getdate() as date))
and dateonly < dateadd(day, 1, cast(getdate() as date));
--6months
SELECT SUM(amount) AS total
FROM tbl_expense_record
WHERE dateonly >= dateadd(month, -6, cast(getdate() as date))
and dateonly < dateadd(day, 1, cast(getdate() as date));
if not exists
(
select *
FROM tbl_expense_record
WHERE dateonly >= dateadd(month, -1, cast(getdate() as date))
and dateonly < dateadd(day, 1, cast(getdate() as date))
)
begin
select 'no rows within the last month'
end
else
begin
select 'there are rows within the last month';
end;
Examples:
declare #tbl_expense_record table(dateonly date, amount decimal(9,2));
insert into #tbl_expense_record
values ('20200501', 10), ('20200612', 10), ('20200613', 11), ('20200614', 12),
('20200710', 5), ('20200720', 6), ('20200820', 20), ('20200825', 30),
('20201102', 1), ('20201110', 2), ('20201120', 3);
--aggregation per month, for all rows
select year(dateonly) as _year, month(dateonly) as _month, sum(amount) as sum_amount_per_month, count(*) as rows_per_month
from #tbl_expense_record
group by year(dateonly), month(dateonly);
--aggregation per iso-week
select year(dateonly) as _year, datepart(iso_week, dateonly) as _isoweek, sum(amount) as sum_amount_per_isoweek, count(*) as rows_per_isoweek
from #tbl_expense_record
group by year(dateonly), datepart(iso_week, dateonly);
--aggregation per month, for all rows with a dateonly that falls in the last month
--check the difference between aggregation per month earlier and this..
--filter rows first == where .... and then aggregate
--there are two rows with dateonly > 06 november (the row at 05 is filtered out by the where clause)
select year(dateonly) as _year, month(dateonly) as _month, sum(amount) as sum_amount_per_month, count(*) as rows_per_month
from #tbl_expense_record
where dateonly >= dateadd(month, -1, cast(getdate() as date))
and dateonly < dateadd(day, 1, cast(getdate() as date))
group by year(dateonly), month(dateonly);
--aggregate per week diff from today/getdate()
select
datediff(week, getdate(), dateonly) as week_diff_from_today,
dateadd(day,
--datepart(weekday..) is used...account for ##datefirst setting / set datefirst
1-(##datefirst+datepart(weekday, dateadd(week, datediff(week, getdate(), dateonly), cast(getdate() as date))))%7,
dateadd(week, datediff(week, getdate(), dateonly), cast(getdate() as date)))
as startofweek,
dateadd(day, 6, --add 6 days to startofweek
dateadd(day,
--datepart(weekday..) is used...account for ##datefirst setting / set datefirst
1-(##datefirst+datepart(weekday, dateadd(week, datediff(week, getdate(), dateonly), cast(getdate() as date))))%7,
dateadd(week, datediff(week, getdate(), dateonly), cast(getdate() as date)))
) as endofweek,
sum(amount) as sum_amount, count(*) as rows_within_week
from #tbl_expense_record
group by datediff(week, getdate(), dateonly);

How to fix the SELECT that use a virtual Table using Group by

I would like to get the count of EventType by month.
I've trying the next 2 possibility:
First possibility
DECLARE #StartDate date
SET #StartDate = GETDATE() - 365;
WITH theDates AS
(SELECT #StartDate as theDate
UNION ALL
SELECT DATEADD(MONTH, 1, theDate)
FROM theDates
WHERE DATEADD(MONTH, 1, theDate) <= GETDATE()
)
SELECT 'Diagnosis' as EventType,
MONTH(theDate),
YEAR(theDate),
concat(MONTH(theDate),'/', YEAR(theDate)),
Count(fpd.DateOfServiceID) as Eventcount
FROM theDates dd
LEFT JOIN fact.FactPatientDiagnosis fpd ON fpd.DateOfServiceID = concat(YEAR(theDate), MONTH(theDate), DAY(theDate))
GROUP BY YEAR(dd.theDate), MONTH(dd.theDate)
I've getting the value 0 for the EventCount Column
But i know that are in the table more than 500 Diagnosis
Second Possibility
DECLARE #StartDate date
SET #StartDate = GETDATE() - 365;
WITH theDates AS
(SELECT #StartDate as theDate
UNION ALL
SELECT DATEADD(MONTH, 1, theDate)
FROM theDates
WHERE DATEADD(MONTH, 1, theDate) <= GETDATE()
)
SELECT 'Diagnosis' as EventType,
MONTH(theDate),
YEAR(theDate),
concat(MONTH(theDate),'/', YEAR(theDate)),
Count(fpd.DateOfServiceID) as Eventcount
FROM theDates dd
LEFT JOIN fact.FactPatientDiagnosis fpd ON fpd.DateOfServiceID is not NULL
GROUP BY YEAR(dd.theDate), MONTH(dd.theDate)
In this Select, I've getting the value 503 for the EventCount column
I assume you want:
SET #StartDate = GETDATE() - 365;
WITH theDates AS (
SELECT #StartDate as theDate
UNION ALL
SELECT DATEADD(MONTH, 1, theDate)
FROM theDates
WHERE DATEADD(MONTH, 1, theDate) <= GETDATE()
)
SELECT 'Diagnosis' as EventType,
YEAR(theDate), MONTH(theDate),
concat(MONTH(theDate), '/', YEAR(theDate)),
Count(fpd.DateOfServiceID) as Eventcount
FROM theDates dd LEFT JOIN
fact.FactPatientDiagnosis fpd
ON fpd.DateOfServiceID >= dd.theDate AND
fpd.DateOfServiceID < DATEADD(month, 1, dd.theDate)
GROUP BY YEAR(dd.theDate), MONTH(dd.theDate)
ORDER BY YEAR(dd.theDate), MONTH(dd.theDate);
You are generating 1 date for each month in CTE (picking current DAY), and comparing it with dateofserviceid, even some date matches, every day you will get different output.
Ideally you should be comparing only YEAR and MONTH part for the JOIN.
LEFT JOIN fact.factpatientdiagnosis fpd ON
month(fpd.dateofserviceid) = month(thedate)
AND
year(fpd.dateofserviceid) = year(thedate)
Your overall query should look like following now.
DECLARE #StartDate DATE
SET #StartDate = Getdate() - 365;
WITH thedates
AS (SELECT #StartDate AS theDate
UNION ALL
SELECT Dateadd(month, 1, thedate)
FROM thedates
WHERE Dateadd(month, 1, thedate) <= Getdate())
SELECT 'Diagnosis' AS EventType,
Month(thedate),
Year(thedate),
Concat(Month(thedate), '/', Year(thedate)),
Count(fpd.dateofserviceid) AS Eventcount
FROM thedates dd
LEFT JOIN fact.factpatientdiagnosis fpd
ON Month(fpd.dateofserviceid) = Month(thedate)
AND Year(fpd.dateofserviceid) = Year(thedate)
GROUP BY Year(dd.thedate),
Month(dd.thedate)
Why would you ever want to use Int for a date IDK. I assume your DateOfServiceID is a date representation in int format like 20180102 (yyyyMMdd). If so:
WITH myData (theDate)
AS (SELECT CONVERT(DATE, CAST(DateOfServiceId AS VARCHAR(8)), 112)
FROM fact.FactPatientDiagnosis
WHERE DateOfServiceId >= CAST(CONVERT(VARCHAR(10), DATEADD(DAY, -365, CAST(GETDATE() AS DATE)), 112) AS INT))
SELECT 'Diagnosis' AS EventType,
MONTH(theDate) AS mn,
YEAR(theDate) AS yr,
SUBSTRING(CONVERT(VARCHAR(10), theDate, 103), 4, 7) AS MonthYear,
COUNT(theDate) AS Eventcount
FROM myData
GROUP BY MONTH(theDate),
YEAR(theDate),
SUBSTRING(CONVERT(VARCHAR(10), theDate, 103), 4, 7)
ORDER BY YEAR(theDate),
MONTH(theDate);

Get attendance by month name

I am trying to find attendance for any month like this:
DECLARE #StartDate DATE
DECLARE #EndDate DATE
SET #StartDate = '2016/10/01'
SET #EndDate = '2016/10/31'
SELECT
StaffAttendance.StaffId, DATENAME(MONTH, #StartDate) AS [ForMonth],
(DATEDIFF(dd, #StartDate, #EndDate) + 1)
-(DATEDIFF(wk, #StartDate, #EndDate) * 1)
-(CASE WHEN DATENAME(dw, #StartDate) = 'Sunday' THEN 1 ELSE 0 END) AS TotalWorkingDays,
SUM(StaffAttendance.AttendanceStatusId) AS TotalDaysWorked FROM StaffAttendance WHERE [Date] BETWEEN #StartDate AND #EndDate AND StaffAttendance.AttendanceStatusId = 1 GROUP BY StaffAttendance.StaffId
And this is what I am getting, which is just fine.
What I want to do next is omit the date range (#StartDate DATE, #EndDate DATE) and use month name instead like - 'October' and not month number like '10.' So that user gives a month name and start and end dates for that month are automatically calculated. I got help for that by Felix Pamittan.
DECLARE #month VARCHAR(9) = 'february';
WITH CteMonths(n, m) AS(
SELECT 1, 'January' UNION ALL
SELECT 2, 'February' UNION ALL
SELECT 3, 'March' UNION ALL
SELECT 4, 'April' UNION ALL
SELECT 5, 'May' UNION ALL
SELECT 6, 'June' UNION ALL
SELECT 7, 'July' UNION ALL
SELECT 8, 'August' UNION ALL
SELECT 9, 'September' UNION ALL
SELECT 10, 'October' UNION ALL
SELECT 11, 'November' UNION ALL
SELECT 12, 'December'
)
SELECT
DATEADD(MONTH, n - 1, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)) AS StartDate,
DATEADD(DAY, -1, DATEADD(MONTH, n, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0))) AS EndDate
FROM CteMonths
WHERE m = #month
Now, how do I combine these two and get same result as above?
Use CROSS APPLY:
DECLARE #month VARCHAR(9) = 'february';
WITH CteMonths(n, m) AS(
SELECT 1, 'January' UNION ALL
SELECT 2, 'February' UNION ALL
SELECT 3, 'March' UNION ALL
SELECT 4, 'April' UNION ALL
SELECT 5, 'May' UNION ALL
SELECT 6, 'June' UNION ALL
SELECT 7, 'July' UNION ALL
SELECT 8, 'August' UNION ALL
SELECT 9, 'September' UNION ALL
SELECT 10, 'October' UNION ALL
SELECT 11, 'November' UNION ALL
SELECT 12, 'December'
)
SELECT
sa.StaffId,
DATENAME(MONTH, t.StartDate) AS [ForMonth],
(DATEDIFF(dd, t.StartDate, t.EndDate) + 1)
- (DATEDIFF(wk, t.StartDate, t.EndDate) * 1)
- (CASE WHEN DATENAME(dw, t.StartDate) = 'Sunday' THEN 1 ELSE 0 END) AS TotalWorkingDays,
SUM(sa.AttendanceStatusId) AS TotalDaysWorked
FROM StaffAttendance sa
CROSS APPLY(
SELECT
DATEADD(MONTH, n - 1, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0)) AS StartDate,
DATEADD(DAY, -1, DATEADD(MONTH, n, DATEADD(YEAR, DATEDIFF(YEAR, 0, GETDATE()), 0))) AS EndDate
FROM CteMonths
) t
WHERE
sa.[Date] >= t.StartDate
AND sa.[Date] < DATEADD(DAY, 1, t.EndDate)
AND sa.AttendanceStatusId = 1
GROUP BY sa.StaffId

SSRS loop over select by months

I am creating report in SSRS that should show event duration in a month. This report should do it 12 time for every calendar month separately. At the moment my select has 12 hardcoded selects, for every month each. How can I do this with one select repeated 12 times and just iterated start and end times, either in SQL language (applicable as a view) or even better from SSRS? Thank you.
SELECT DATEDIFF(mi, '2014-01-01 00:00:00.000', end_date) AS total_event
FROM MY_TABLE
WHERE (start_date <= '2014-01-01 00:00:00.000') AND (end_date BETWEEN '2014-01-01 00:00:00.000' AND '2014-02-01 00:00:00.000')
UNION ALL
SELECT DATEDIFF(mi, start_date, '2014-02-01 00:00:00.000') AS total_event
FROM MY_TABLE
WHERE (end_date >= '2014-02-01 00:00:00.000') AND (start_date BETWEEN '2014-01-01 00:00:00.000' AND '2014-02-01 00:00:00.000')
UNION ALL
SELECT DATEDIFF(mi, '2014-01-01 00:00:00.000', '2014-02-01 00:00:00.000') AS total_event
FROM MY_TABLE
WHERE (start_date <= '2014-01-01 00:00:00.000') AND (end_date >= '2014-02-01 00:00:00.000')
UNION ALL
SELECT DATEDIFF(mi, start_date, end_date) AS total_event
FROM MY_TABLE
WHERE (start_date BETWEEN '2014-01-01 00:00:00.000' AND '2014-02-01 00:00:00.000') AND (end_date BETWEEN '2014-01-01 00:00:00.000' AND '2014-02-01 00:00:00.000')
try using below script
declare #start datetime
declare #end datetime
set #start = '2014-01-01' --firstDay year
set #end = '2014-12-31' --last Day of year
;with cte as
(
select #start firstday, DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,DATEADD(month, DATEDIFF(month, 0, #start), 0))+1,0)) lastday, 1 MM
union all
select DATEADD(month, DATEDIFF(month, 0, lastday + 1), 0) , DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,DATEADD(month, DATEDIFF(month, 0, lastday + 1), 0))+1,0)), MM + 1
from cte
where lastday < #end
)
SELECT * FROM CTE C
--LEFT OUTER JOIN MY_TABLE M ON (M.start_date <= firstday) AND (end_date BETWEEN firstday AND lastday)
just un-comment last line
--LEFT OUTER JOIN MY_TABLE M ON (M.start_date <= firstday) AND (end_date BETWEEN firstday AND lastday)
You could try this:
SELECT
DATEADD(MONTH, DATEDIFF(MONTH, 0, start_date), 0) AS [month], DATEDIFF(MINUTE, DATEADD(MONTH, DATEDIFF(MONTH, 0, start_date), 0), end_date) AS total_event
FROM
MY_TABLE
WHERE
YEAR(start_date) = 2014
ORDER BY
[month];

how to get the start and end dates of all weeks between two dates in SQL server?

I need to get all week start and end dates(weeks) between two dates and then run a query returning the number of records inserted in each of those weeks.
declare #sDate datetime,
#eDate datetime;
select #sDate = '2013-02-25',
#eDate = '2013-03-25';
--query to get all weeks between sDate and eDate
--query to return number of items inserted in each of the weeks returned
WEEK NoOfItems
-----------------------------------------
2013-02-25 5
2013-03-4 2
2013-03-11 7
You can use a recursive CTE to generate the list of dates:
;with cte as
(
select #sDate StartDate,
DATEADD(wk, DATEDIFF(wk, 0, #sDate), 6) EndDate
union all
select dateadd(ww, 1, StartDate),
dateadd(ww, 1, EndDate)
from cte
where dateadd(ww, 1, StartDate)<= #eDate
)
select *
from cte
See SQL Fiddle with Demo.
Then you can join this to your table, to return the additional details.
Here is my solution. Inspired by this answer
DECLARE #sDate DATE = DATEADD(MONTH, -6, GETDATE())
DECLARE #eDate DATE = GETDATE()
;WITH cte AS
(
SELECT DATEADD(WEEK, DATEDIFF(WEEK, 0, #sDate), 0) AS StartDate, DATEADD(WEEK, DATEDIFF(WEEK, 0, #sDate), 6) AS EndDate
UNION ALL
SELECT DATEADD(WEEK, 1, StartDate), DATEADD(WEEK, 1, EndDate)
FROM cte
WHERE DATEADD(WEEK, 1, StartDate) <= #eDate
)
SELECT * FROM cte

Resources