Running data for 1 year dates in SQL Server - 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;

Related

SQL search for occurrences of transaction types within multiple date ranges

I have the following tables:
CREATE TABLE Users
(
UserID INT,
UserName VARCHAR(100)
)
CREATE TABLE Trans
(
UserID INT,
TransID INT,
TransTypeID INT,
TransDate DATETIME,
TransAmount DECIMAL(15,2)
)
CREATE TABLE TransType
(
TransTypeID INT,
TransTypeName VARCHAR(10)
)
Lets say for simplicity there are only 2 entries in TransType table:
Income
Expense
I'm looking to find out if 3 or more transactions of type Income AND 3 or more transactions of type Expense occurred within a particular time period over a date range for a particular user.
So for example, my search could have the following variables:
DECLARE #StartDate DATETIME = '2018-06-30'
DECLARE #EndDate DATETIME = '2019-09-30'
DECLARE #SearchPeriodMonths INT = 6
My current solution searches in blocks of 6 months (as defined by #SearchPeriodMonths) from #StartDate to #EndDate and if 3 or more transactions of Income and Expense exist, then this would need to be returned in a table. This solution is not very efficient and involves using a cursor. I'm hoping there is a better way of going about it.
CREATE TABLE #Results
(
UserID int,
StartDate DATETIME,
EndDate DATETIME,
Message VARCHAR(255)
)
DECLARE #UserID INT
DECLARE crUser CURSOR FOR
SELECT UserID
FROM Users
OPEN crUser
FETCH NEXT FROM crUser INTO #UserID
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #DateCount TABLE(UserID INT, StartDate DATETIME, EndDate DATETIME, IncomeCount INT, ExpenseCount INT)
WHILE(#StartDate < #EndDate)
BEGIN
SELECT #StartDate = DATEADD(DAY, 1, #StartDate)
INSERT INTO #DateCount
SELECT
#UserID AS UserID,
#StartDate AS StartDate,
DATEADD(MONTH, #SearchPeriodMonths, #StartDate) AS EndDate,
(SELECT COUNT(*) FROM Trans WHERE TransTypeID = 1 AND UserID = #UserID
AND TransDate BETWEEN #StartDate and DATEADD(MONTH, #SearchPeriodMonths, #StartDate)
AS IncomeCount,
(SELECT COUNT(*) FROM Trans WHERE TransTypeID = 2 AND UserID = #UserID
AND TransDate BETWEEN #StartDate and DATEADD(MONTH, #SearchPeriodMonths, #StartDate)
AS ExpenseCount
END
INSERT INTO #Results
SELECT
#UserID AS UserID,
MIN(StartDate) AS StartDate,
MAX(EndDate) AS EndDate,
'3 or more Income and Expense transaction types occur in a 6 month period between '
+ convert(varchar, min(StartDate), 106) + ' and '
+ convert(varchar, max(EndDate), 106) AS WarningMessage
FROM #DateCount
WHERE IncomeCount > 2 AND ExpenseCount > 2
FETCH NEXT FROM crUser INTO #UserID
END
CLOSE crUser
DEALLOCATE crUser
-- Output query
SELECT *
FROM #Results
END
I am restricted to doing this in SQL Server 2008 R2. Is there a more efficient way to do this which doesn't involve using a cursor?
I'm looking to find out if 3 or more transactions of type Income AND 3 or more transactions of type Expense occurred within a particular time period over a date range for a particular user.
Use aggregation!
select userid
from trans
where transdate between #startdate and dateadd(month, #SearchPeriodMonths, #StartDate)
group by userid
having sum(case when transtypeid = 1 then 1 else 0 end) >= 3 and
sum(case when transtypeid = 2 then 1 else 0 end) >= 3;

Find out the quarter based on the financial year in SQL Server stored procedure

I have two tables, one is Period and the other is UserTarget.
Period table is:
QuarterNo StartDate EndDate
-----------------------------------------
1 2018-04-01 2018-06-30
2 2018-07-01 2018-09-30
3 2018-10-01 2018-12-31
4 2019-01-01 2019-03-31
UserTarget table is :
USERID YEAR QTR AMOUNT
---------------------------------
akshay 2019 1 200
Right now I am taking the qtr no from period table. Now I don't need take qtr no from period table. I want it from stored procedure based on year is entered in usertarget table
Existing stored procedure :
ALTER PROCEDURE [dbo].[GetQuarterlyTargetData]
#Userid VARCHAR(50)
AS
BEGIN
DECLARE #QuarterNumber VARCHAR(50)
DECLARE #SetTarget DECIMAL(10);
DECLARE #StartDate DATE
DECLARE #EndDate DATE
SELECT
#QuarterNumber = p.QuarterNo,
#SetTarget = AMOUNT
FROM
PERIOD p
LEFT OUTER JOIN
USERTARGETS s ON p.QuarterNo = s.QTR
WHERE
StartDate <= GETDATE() AND EndDate >= GETDATE()
SELECT
#StartDate = StartDate,
#EndDate = EndDate
FROM
PERIOD
WHERE
QuarterNo = #QuarterNumber
From this procedure I am getting the start date and end date for quarter but I don't want to modify in period table every time when I want to check previous years data.
I believe that the term you are looking for is fiscal year. It's where the company year is different than the calendar year.
Note that many people recommend using a lookup table instead of calculating it. Date matches can be difficult for SQL to optimize.
Here's one way to do it. Finding the fiscal year and quarter would probably be good to put in a table function.
DECLARE #userTarget TABLE (UserId VARCHAR(20), Year INT, Quarter INT, Amount INT)
INSERT INTO #userTarget
VALUES
('akshay', 2018, 4, 150)
,('akshay', 2019, 1, 200)
SELECT
s.UserId
,s.Amount
,FY.FiscalYear
,FQ.FiscalQuarter
FROM
(
SELECT
--DATEFROMPARTS(2019, 2, 23)
GETDATE()
AS reportDate
) AS ReportDate
CROSS APPLY (
SELECT
CASE WHEN MONTH(ReportDate.reportDate) < 4 THEN YEAR(ReportDate.reportDate) - 1 -- Fiscal Year begins in April
ELSE YEAR(ReportDate.reportDate)
END AS FiscalYear
) AS FY
CROSS APPLY (
SELECT
DATEDIFF(QUARTER, DATEFROMPARTS(FY.FiscalYear, 4, 1), ReportDate.reportDate) + 1 AS FiscalQuarter
) AS FQ
INNER JOIN #userTarget s
ON s.Year = FY.FiscalYear
AND s.Quarter = FQ.FiscalQuarter
Also, be careful with end dates. last_day >= GETDATE() does not include the last day. Take as an example the end of last quarter, it would calculate it as '2019-03-31 00:00' >= '2019-03-31 08:20' which is false when you want it to be true.
After thinking I come up to this solutions
ALTER PROCEDURE [dbo].[Dashboard_GetQuarterlyTargetData]
#Userid varchar(50)
AS
Begin
DECLARE #QuarterNumber varchar(50)
DECLARE #SetTarget decimal(10);
DECLARE #AchievedTarget decimal(10);
DECLARE #TargetProgress decimal(10);
DECLARE #RemainingTarget decimal(10);
DECLARE #StartDate Date
DECLARE #EndDate Date
DECLARE #Year as int
Select #QuarterNumber = QTR,#Year=YEAR,#SetTarget=AMOUNT from USERTARGETS where USERID=#Userid
if(#QuarterNumber = 1)
begin
SELECT #StartDate = DATEFROMPARTS(#year,4,1), #EndDate=DATEFROMPARTS(#year,6,30)
End
else if(#QuarterNumber = 2)
begin
SELECT #StartDate = DATEFROMPARTS(#year,7,1), #EndDate=DATEFROMPARTS(#year,9,30)
End
else if(#QuarterNumber = 3)
begin
SELECT #StartDate = DATEFROMPARTS(#year,10,1), #EndDate=DATEFROMPARTS(#year,12,31)
End
else if(#QuarterNumber = 4)
begin
SELECT #StartDate = DATEFROMPARTS(#year,1,1), #EndDate=DATEFROMPARTS(#year,3,31)
End

Get monthly attendance for an employee

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')

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.

Getting values from Database instead of using Declare

I am using
declare #starttime datetime = '2015-10-28 10:00',
#endtime datetime = '2015-10-28 12:00',
#interval int = 30
I would like to retrieve these values from a table instead of declaring them as static values. I have a separate table that has these 3 values (Startdate, Enddate, interval).
I tried creating a temp table an then inserting values into it but the naming convention fails to work if I want to use the vales in my further query. My entire query is this and in this i want the startdate, enddate,interval to be fetched from the database instead of being static.
create table #booking (start datetime, [end] datetime)
insert into #booking values
('2015-10-28 08:00','2015-10-28 08:30'),
('2015-10-28 10:00','2015-10-28 10:30'),
('2015-10-28 10:30','2015-10-28 11:00')
declare #starttime datetime = '2015-10-28 08:00',
#endtime datetime = '2015-10-28 12:00',
#interval int = 30,
#slots int
select #slots = datediff(minute, #starttime, #endtime)/#interval
SELECT TOP (#slots) N=IDENTITY(INT, 1, 1)
INTO #Numbers
FROM master.dbo.syscolumns a CROSS JOIN master.dbo.syscolumns b;
select
dateadd(minute,((n-1)*#interval),#starttime) as start,
dateadd(minute,(n*#interval),#starttime) as [end]
into
#slots
from
#numbers
select s.*, b.* from #slots s
left join #booking b
on s.start = b.start and s.[end] = b.[end]
where b.start is null
drop table #numbers, #booking, #slots
I'm not so sure I understand the question, but assuming you can fetch a single row from that table, you could just do this:
declare #starttime datetime
,#endtime datetime
,#interval int
select #starttime = starttime
,#endtime = endtime
,#interval = interval
from yourTable
where <condition>
You can do something like:
SELECT
#starttime = Startdate
, #endtime = Enddate
, #interval = interval
FROM YourTableHere

Resources