Get monthly attendance for an employee - sql-server

I have a query that a guy helped here in developing which gives fine result. I want to use this query in a function which later I want to use in a procedure.
CREATE FUNCTION [dbo].[fnGetWorkedDays] (#StartDate datetime, #EndDate datetime)
RETURNS int
AS
BEGIN
DECLARE #dateFrom datetime
DECLARE #dateTo datetime
SET #dateFrom = #StartDate
SET #dateTo = #EndDate
DECLARE #DAYSWORKED INT
SELECT #DAYSWORKED = (
SELECT EmpId, COUNT(*) as DaysWorked
FROM
(
SELECT DISTINCT EmpId,CAST(TimeIn AS DATE) AS [Date]
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
)T
GROUP BY EmpId
ORDER BY EmpId)
RETURN ISNULL(#DAYSWORKED,0)
END
The very first error I am getting is that
Msg 1033, Level 15, State 1, Procedure fnGetWorkedDays, Line 24
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.
I removed ORDER BY then I got another error says
Msg 116, Level 16, State 1, Procedure fnGetWorkedDays, Line 25
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.

I suggest you to go though this to understand more on table valued functions vs scalar valued function Difference between scalar, table-valued, and aggregate functions in SQL server?
Regarding your scenario, you should be writing a table values function for this like following.
CREATE FUNCTION [dbo].[fnGetWorkedDays]
(
#StartDate AS DATETIME,
#EndDate datetime
)
RETURNS TABLE
AS
RETURN
SELECT EmpId, COUNT(*) as DaysWorked
FROM
(
SELECT DISTINCT EmpId,CAST(TimeIn AS DATE) AS [Date]
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
)T
GROUP BY EmpId
To use the function you can write your queries like following.
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2018-01-01'
SET #EndDate = '2018-01-31'
select * from [dbo].[fnGetWorkedDays](#StartDate,#EndDate)
If you want this to work as a scalar value function, in that case you need to pass EmpId as parameter like following.
CREATE FUNCTION [dbo].[fnGetWorkedDaysEmpWise]
(
#StartDate AS DATETIME,
#EndDate datetime,
#EmpId INT
)
RETURNS INT
AS
BEGIN
DECLARE #RetunCount INT
SELECT #RetunCount = COUNT(DISTINCT CAST(TimeIn AS DATE))
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
AND EmpID=#EmpId
RETURN #RetunCount
END
And you can use it directly in your select clause like following.
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SET #StartDate = '2018-01-01'
SET #EndDate = '2018-01-31'
SELECT [dbo].[fnGetWorkedDaysEmpWise](#StartDate,#EndDate,1)

Is this what you are looking for .?
CREATE FUNCTION [dbo].[fnGetWorkedDays] (#StartDate datetime, #EndDate datetime)
RETURNS #DaysWorked TABLE
(
Empid Int,
DaysWorked Int
)
AS
BEGIN
insert into #DaysWorked (Empid,DaysWorked)
SELECT EmpId, COUNT(*) as DaysWorked
FROM
(
SELECT DISTINCT EmpId,CAST(TimeIn AS DATE) AS [Date]
FROM myTable
WHERE TimeIn IS NOT NULL
AND CAST(TimeIn AS DATE) BETWEEN #StartDate AND #EndDate
)T
GROUP BY EmpId
ORDER BY EmpId
RETURN
END
select * from [fnGetWorkedDays]('2018-01-01','2018-01-31')

Related

Running data for 1 year dates in SQL Server

I have a procedure where I have calls data for each day. From this procedure I wrote another procedure to insert and update in the destination table.
We have a job which runs for every day. But now I need to run the data for past 1 year (each day).
Below are my parameters in insert update procedure - if I need to get data for 18th my parameters values are like below
,#StartDate datetime = '2020-04-18'
,#EndDate datetime = '2020-04-19'
,#SkillLevel varchar(10)='Total'
,#Region varchar(20) = 'US'
If I need to get data for 17th my parameters values are like below
,#StartDate datetime = '2020-04-17'
,#EndDate datetime = '2020-04-18'
,#SkillLevel varchar(10)='Total'
,#Region varchar(20) = 'US'
Like this to get data for last 1 year I need to run the code for 365 days which takes huge effort
Could anyone suggest how to pass dates individually for 1 year with some loop.
Try this:
DECLARE #DataSource TABLE
(
[StartDate] DATE
);
DECLARE #BegDate DATE
,#EndDate DATE;
SELECT #BegDate = '2020-01-01'
,#EndDate = '2020-12-31';
DECLARE #EndNumber BIGINT;
SELECT #EndNumber = DATEDIFF(DAY, #BegDate, #EndDate);
INSERT INTO #DataSource ([StartDate])
SELECT TOP (#EndNumber) DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY T1.[number])-1, #BegDate)
FROM master..spt_values T1
CROSS JOIN master..spt_values T2;
WHILE EXISTS(SELECT 1 FROM #DataSource)
BEGIN;
SELECT TOP 1 #BegDate = [StartDate]
,#EndDate = DATEADD(DAY, 1, [StartDate])
FROM #DataSource;
--
EXEC [stored_precedure] #StartDate = #BegDate
,#EndDate = #EndDate
,#SkillLevel ='Total'
,#Region = 'ES'
--
DELETE FROM #DataSource
WHERE [StartDate] = #BegDate;
END;

How to create date table with given datetime range?

I'm trying to create a table for calendar.But sql server stops at '2000-01-11 17:45'. How can i do create dates adding 15 minutes until 2050
Create Table Calendar
(id int IDENTITY(1,1) Primary key,CalendarDate DATETIME)
Declare #beginDate DATETIME, #endDate DATETIME
Select #beginDate = '2000-01-01 17:45', #endDate = '2050-01-01 09:00'
While #beginDate <= #endDate
Begin
Insert Into dbo.Calendar(CalendarDate)
Select
#beginDate As CalendarDate
Set #beginDate = DateAdd(MINUTE, 15, #beginDate)
End
Your current syntax suggest SQL Server, so i would try with recursive CTE :
with cte as (
select #beginDate as st
union all
select DATEADD(MINUTE, 15, st)
from cte
where st < #endDate
)
Insert Into dbo.Calendar(CalendarDate)
select st as CalendarDate
from cte
option (maxrecursion 0);

CTE, Incorrect syntax near ';' SQL SERVER

I've written such PROCEDURE in Sql Server:
CREATE PROCEDURE p_SelectDatesFromRange
(
#startDate DATETIME,
#endDate DATETIME
)
AS
(
;WITH dates AS
(
SELECT #startDate AS [Date]
UNION ALL
SELECT DATEADD(day, 1, [Date])
FROM dates
WHERE [Date] <= #endDate - 1
)
SELECT *
FROM dates
);
It should return all dates, between two, given parameters. But I can't run it, it gives me an error.
Incorrect syntax near ';'
Incorrect syntax near ')'
This errors are at the beginning of 'with', an at the end of code of procedure.
I'm learning CTE, so sorry, if this is obvious mistake.
Please help, thanks, Mike.
I'm not sure that your error has anything to do with CTEs per se, but rather you are trying to use parentheses to denote the body of your stored procedure. Either drop the parentheses, or possibly use a BEGIN and END block:
CREATE PROCEDURE p_SelectDatesFromRange
(
#startDate DATETIME,
#endDate DATETIME
)
AS
BEGIN
;WITH dates AS
(
SELECT #startDate AS [Date]
UNION ALL
SELECT DATEADD(day, 1, [Date])
FROM dates
WHERE [Date] <= #endDate - 1
)
SELECT *
FROM dates
END;
This works
CREATE PROCEDURE DBO.DateRange
#start datetime,
#end datetime
AS
with cte as
( SELECT #start as strtDt, #end as endDt
union all
select strtDt + 1, endDt
from cte
where strtDt < endDt
)
select * from cte

Group data by interval of 15 minutes and use cross tab

I am trying to write a query based on datetime and weekday in sql server where my output should be like :
My table descriptions are:
**Branch**(DateKey integer,
BranchName varchar2(20),
TransactionDate datetime,
OrderCount integer)
**Date**(DateKey integer PrimaryKey,
DayNameofWeek varchar2(15))
This is the raw data I have
So, this is quite a long shot but I solved it the following way:
I created a table valued function, which would take a date as a parameter and find all 15-minute intervals during that day.
For each day it would go from 00:00, to 00:15, 00:30 up to 23:30, 23:45, and 23:59. It also returns each interval start time and end time, since we will need to use this for every row in your branch table to check if they fall into that time slot and if so, count it in.
This is the function:
create function dbo.getDate15MinIntervals(#date date)
returns #intervals table (
[interval] int not null,
[dayname] varchar(20) not null,
interval_start_time datetime not null,
interval_end_time datetime not null
)
as
begin
declare #starttime time = '00:00';
declare #endtime time = '23:59';
declare #date_start datetime;
declare #date_end datetime;
declare #min datetime;
select #date_start = cast(#date as datetime) + cast(#starttime as datetime), #date_end = cast(#date as datetime) + cast(#endtime as datetime);
declare #minutes table ([date] datetime)
insert into #minutes values (#date_start), (#date_end) -- begin, end of the day
select #min = DATEADD(mi, 0, #date_start)
while #min < #date_end
begin
select #min = DATEADD(mi, 1, #min)
insert into #minutes values (#min)
end
insert into #intervals
select ([row]-1)/15+1 intervalId, [dayname], min(interval_time) interval_start_time
> -- **NOTE: This line is the only thing you need to change:**
, DATEADD(ms, 59998, max(interval_time)) interval_end_time
from
(
select row_number() over(order by [date]) as [row], [date], datename(weekday, [date]) [dayname], [date] interval_time
from #minutes
) t
group by ([row]-1)/15+1, [dayname]
order by ([row]-1)/15+1
return
end
--example of calling it:
select * from dbo.getDate15MinIntervals('2017-07-14')
Then, I am querying your branch table (you don't really need the Date table, the weekday now you have it in the function but even if not, there's a DATENAME function in SQL Server, starting with 2008 that you can use.
I would query your table like this:
select branchname, [dayname], ISNULL([11:30], 0) as [11:30], ISNULL([11:45], 0) as [11:45], ISNULL([12:00], 0) as [12:00], ISNULL([12:45], 0) as [12:45]
from
(
select intervals.[dayname]
, b.branchname
, convert(varchar(5), intervals.interval_start_time, 108) interval_start_time -- for hh:mm format
, sum(b.ordercount) ordercount
from branch b cross apply dbo.getDate15MinIntervals(CAST(b.TransactionDate as date)) as intervals
where b.transactiondate between interval_start_time and interval_end_time
group by intervals.[dayname], b.branchname, intervals.interval_start_time, intervals.interval_end_time
) t
pivot ( sum(ordercount) for interval_start_time in ( [11:30], [11:45] , [12:00], [12:45] )) as p
Please note I have used in the PIVOT function only the intervals I can see in the image you posted, but of course you could write all 15-minute intervals of the day manually - you would just need to write them once in the pivot and once in the select statement - or optionally, generate this statement dynamically.

SQL server SP : #Param 's with sometime NULL values

I am very new to SQL Server Stored Procedures,
I am trying to create a SP that will give return a list of records in a table by filter via StartDate and EndDate , but there will be 'View All' Option so sometime those #Param might not contain any values.
Currently my SP is Like
CREATE PROCEDURE [dbo].[spGetBonusRun]
(
#StartDate as DATETIME,
#EndDate as DATETIME
)
AS
SELECT [Id]
,[StartDateTime]
,[EndDate]
,[Status]
FROM [Valt].[dbo].[BonusRun]
WHERE StartDateTime <= #StartDate AND EndDate >= #EndDate
How to active that ?
Try this:
WHERE (StartDateTime <= #StartDate OR #StartDate IS NULL) AND (EndDate >= #EndDate OR #EndDate IS NULL)
Hope it helps.
/Klaus
You can try something like this
CREATE PROCEDURE [dbo].[spGetBonusRun]
(
#StartDate as DATETIME,
#EndDate as DATETIME
)
AS
SELECT [Id]
,[StartDateTime]
,[EndDate]
,[Status]
FROM [Valt].[dbo].[BonusRun]
WHERE StartDateTime <= ISNULL(#StartDate, StartDateTime)
AND EndDate >= ISNULL(#EndDate, EndDate)
Note the use of ISNULL

Resources