I have this sp to make a report which needs to be daily. How can I implement the day part? It is OK like I wrote or that is some easy way?!
ALTER PROCEDURE [dbo].[pr_Report]
#YearOfRegistration INT
AS
SELECT
peCountryID,
peCountryName as coName,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 1 THEN 1 ELSE 0 END ),0) AS rdValue1,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 0 THEN 1 ELSE 0 END ),0) AS rdValue2,
COUNT(*) AS Total
FROM
vPerson
WHERE
#YearOfRegistration = 0
OR peYearOfRegistration = #YearOfRegistration
AND (DATEPART(dd, peSubmitDate) = DATEPART(dd, GETDATE())
AND DATEPART(MM, peSubmitDate) = DATEPART(MM, GETDATE())
AND DATEPART(yy, peSubmitDate) = DATEPART(YY, GETDATE()))
GROUP BY
peCountryofResidencyID, peCountryOfResidencyName
The logic is correct, but it is a really bad way to do it. Wherever possible to you should avoid calling functions on your data, especially in the where clause, because it means that any indexes on the underlying columns can not be used.
Your predicate would be better written as:
WHERE peSubmitDate >= CAST(GETDATE() AS DATE)
AND peSubmitDate < DATEADD(DAY, 1, CAST(GETDATE() AS DATE));
This way indexes can be used, and your query is sargable
As it happens, converting DATETIME to DATE (and vice versa) is actually an exception to the rule of not using functions, so you can shorten this to:
WHERE CONVERT(DATE, peSubmitDate) = CONVERT(DATE, GETDATE())
Another point is that although it might look better to use OR to accommodate both your options (of filtering by year or returning all records), you will find that having two separate queries will perform better. so your final SP might be:
ALTER PROC [dbo].[pr_Report] #YearOfRegistration INT
AS
BEGIN
IF (#YearOfRegistration = 0)
BEGIN
SELECT peCountryID,
peCountryName as coName,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 1 THEN 1 ELSE 0 END ),0) AS rdValue1,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 0 THEN 1 ELSE 0 END ),0) AS rdValue2,
COUNT(*) AS Total
FROM vPerson
WHERE CONVERT(DATE, peSubmitDate) = CONVERT(DATE, GETDATE())
GROUP BY peCountryofResidencyID,peCountryOfResidencyName
END
ELSE
BEGIN
SELECT peCountryID,
peCountryName as coName,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 1 THEN 1 ELSE 0 END ),0) AS rdValue1,
ISNULL(SUM(CASE WHEN peIsSubmittedFL = 0 THEN 1 ELSE 0 END ),0) AS rdValue2,
COUNT(*) AS Total
FROM vPerson
WHERE CONVERT(DATE, peSubmitDate) = CONVERT(DATE, GETDATE())
AND peYearOfRegistration = #YearOfRegistration
GROUP BY peCountryofResidencyID,peCountryOfResidencyName;
END
END
getdate returns a datetime, so if you want to compare just the date and not the time of day, you could use cast. If peSubmitDate is of datatype Date , use this comparison:
peSubmitDate = cast(GetDate() as date)
If it is a datatime then use it like this:
cast(peSubmitDate as date) = cast(GetDate() as date)
The latter one gives worse performance, so only use this if is a datetime
Related
I have date(dt col name) column in est.
If i execute query between '7:30pm'EST and '11:59' Est timezone.i should get previous day information and if i execute after 12:00 i need to get today's information.
I have tried this:
Case when getdate() between '2021-03-09 7:30:00' and '2021-03-09 11:59:00'
Then dt=getdate() -1
else dt=getdate()
If I understand your question correctly, then you want to retrieve different rows depending on the time when the query is executed.
Using a case expression can work, but then you have to move the comparison with the case expression result outside the case expression.
So instead of:
... where case when <condition> then (Column = Value1) else (Column = Value2) end
do this:
... where Column = (case when <condition> then Value1 else Value2 end)
where the parentheses are added here for clarity.
Sample data
create table MyTable
(
dt date,
data nvarchar(20)
);
insert into MyTable (dt, data) values
('2021-03-08', 'yesterday''s data'),
('2021-03-09', 'today''s data');
Solution
Applying the construction from the start of my answer. Unit testing both options of the case expression will require a separate variable (or you could just wait hours and hours...).
Requesting data on today's date in period [07:30, 11:59]
declare #referenceDate smalldatetime = '2021-03-09 10:20';
--> using date variable instead of 'getdate()' for demo purposes !
select mt.dt,
mt.data
from MyTable mt
where mt.dt = case
when #referenceDate between '2021-03-09 07:30:00' and '2021-03-09 11:59:00'
then dateadd(day, -1, convert(date, #referenceDate))
else convert(date, #referenceDate)
end;
--> returns data from yesterday
Requesting data on today's date outside period [07:30, 11:59]
declare #referenceDate smalldatetime = '2021-03-09 17:40';
--> using date variable instead of 'getdate()' for demo purposes !
select mt.dt,
mt.data
from MyTable mt
where mt.dt = case
when #referenceDate between '2021-03-09 07:30:00' and '2021-03-09 11:59:00'
then dateadd(day, -1, convert(date, #referenceDate))
else convert(date, #referenceDate)
end;
--> returns data from today
Requesting data on any date depending on period [07:30, 12:00]
select mt.dt,
mt.data
from MyTable mt
where mt.dt = case
when convert(time, getdate()) >= '07:30:00'
and convert(time, getdate()) < '12:00:00'
then dateadd(day, -1, convert(date, getdate()))
else convert(date, getdate())
end;
--> returns data from today OR yesterday depeding on time of execution (in/outside [07:30, 12:00])
Your question also mentions the EST timezone. If you want the current execution time to be interpreted as EST, then have a look a this question and this documentation page. Whether you should store data in EST instead of UTC in the first place is a whole other question... You will end up with something like this.
-- converting the query reference time to EST
declare #referenceDate smalldatetime = (select getdate() at time zone 'Eastern Standard Time');
select mt.dt,
mt.data
from MyTable mt
where mt.dt = case
when convert(time, #referenceDate) >= '07:30:00'
and convert(time, #referenceDate) < '12:00:00'
then dateadd(day, -1, convert(date, #referenceDate))
else convert(date, #referenceDate)
end;
Fiddle to see everything in action.
Currently my case statement is based on the first day of the current month.
SELECT
[TicketNbr] AS 'Ticket Nbr'
-- 1st day of this month
, ( CASE WHEN [date_entered] >= DATEADD(day,-1, GETDATE()) THEN 1 ELSE 0 END) AS '1=Opened Within Last 24HR 0=No'
FROM [v_rpt_Service] WITH(NOLOCK)
WHERE ([date_entered] >= '2017-04-01T11:24:00.000' AND [date_entered] < '2017-05-01T11:24:00.000')
GROUP BY [TicketNbr]
But I want to modify it so it will adjust so it will based on the date range
in the where clause. (And that can be any date ranges.)
How do I do this without variables?
Sounds like you just need variables
declare #startDate datetime = '2017-04-01 11:24:00'
declare #endDate datetime = '2017-05-01 11:24:00'
SELECT
[TicketNbr] AS 'Ticket Nbr'
-- 1st day of this month
, ( CASE WHEN [date_entered] between #startDate and #endDate THEN 1 ELSE 0 END) AS '1=Opened Within Date Range 0= No'
FROM [v_rpt_Service] WITH(NOLOCK)
WHERE ([date_entered] >= startDate AND [date_entered] < #endDate)
GROUP BY [TicketNbr]
I am trying to calculate AGE of contracts, however I am getting the error
Explicit conversion from data type date to int is not allowed
This is my code:
SELECT
Contract_hist.*,
IB.*,
CAST((CASE
WHEN (CAST(CONVERT(INT, Setup.[Start]) - CONVERT(INT,[IB].[Install_Date])) AS INT))/365 <= 0
THEN 0
WHEN (CAST(CONVERT(INT, Setup.[Start]) - CONVERT(INT,[IB].[Install_Date])) AS INT)) / 365 > 0
THEN (CAST(CONVERT(INT, Setup.[Start]) CONVERT(INT,[IB].[Install_Date])) AS INT)) / 365
END) AS INT) AS AGE
FROM
Setup, Contract_hist
INNER JOIN
IB ON Contract_hist.[System No] = IB.System_ID
Where "Setup.[Start]" & "[IB].[Install_Date]" are datetime2 values.
You could convert those datetime2 fields first to datetime, then to an int.
(convert(int, convert(datetime, Setup.[Start])) - convert(int,convert(datetime, [IB].[Install_Date])))/365
But that can be shortened to:
cast(convert(datetime, Setup.[Start]) - convert(datetime, IB.[Install_Date]) as int)/365
Or you could simplify it even more and calculate the difference in years directly with datediff:
datediff(year, [IB].[Install_Date], Setup.[Start])
And that CASE can also be simplified:
case
when datediff(year, [IB].[Install_Date], Setup.[Start]) > 0
then datediff(year, [IB].[Install_Date], Setup.[Start])
else 0
end as AGE
Maybe this?
SELECT
Contract_hist.*
,IB.*
,CASE
WHEN DATEDIFF(year,[IB].[Install_Date], Setup.[Start] ) <= 0 THEN 0
ELSE DATEDIFF(year, [IB].[Install_Date], Setup.[Start])
END AS AGE
FROM Setup, Contract_hist
INNER JOIN IB ON Contract_hist.[System No] = IB.System_ID
Seems like casting to a float/int is not supported in the latter versions of sql server. Try using DATEDIFF(dd, '12/30/1899', mydatefield)
Is there a function in sql server 2012 which calculates only working days?
I have been searching but with no luck so far.
Thanks!
No, SQL Server doesn't have such functions, but you can use calendar table:
DECLARE #date_start date = '2016-01-01',
#date_end date = '2016-12-31';
WITH cte as (
SELECT #date_start as [d], 0 as Level
UNION ALL
SELECT DATEADD(day,1,[d]), [level] + 1 as [level]
from cte
WHERE [level] < DATEDIFF(day,#date_start,#date_end)
),
holidays as ( --table with holidays (USA)
SELECT * FROM (VALUES
('2016-01-01'),
('2016-01-18'),
('2016-02-15'),
('2016-05-30'),
('2016-07-04'),
('2016-09-05'),
('2016-10-10'),
('2016-11-11'),
('2016-11-24'),
('2016-12-26')) as t(d)
)
SELECT c.d,
CASE WHEN DATEPART(WEEKDAY,c.d) IN (1,7) THEN 0 --Saturday and Sunday, use (6,7) for Friday,Saturday
WHEN h.d IS NOT NULL THEN 0
ELSE 1 END as isWorking
FROM cte c
LEFT JOIN holidays h
ON c.d=h.d
OPTION (MAXRECURSION 1000);
It will generate a table with all dates in 2016 year and flag - is the day working or not.
Below is the high level overview of how you can do this..
Create a dummy table which holds dates in this format...
date isholiday
20160101 1
20160102 0
Now from your main table which holds employees attendance ,join above table like..
select empid,sum(Case when mt.datee is not null then 1 else 0 end) as workingdays
from
dummydatees dt
left join
maintable mt
on dt.datee=mt.datee
where dt.isholiday=0
This script will calculate the total working days excluding Saturday, Sunday and holidays. I have to list all the holidays since I don't have a table for holidays. You can modify it so that it will meet your requirements.
DECLARE #MyCounter int = 0, #TempDate datetime, #EndDate datetime;
SET #TempDate = DATEADD(d,1,'2017-5-27')
SET #EndDate = '2017-6-3'
WHILE #TempDate <= #EndDate
BEGIN
IF DATENAME(DW,#TempDate) = 'Sunday' OR DATENAME(DW,#TempDate) = 'Saturday'
SET #MyCounter = #MyCounter
ELSE IF #TempDate not in ('2017-1-1', '2017-1-16', '2017-2-20', '2017-5-29', '2017-7-4', '2017-9-4', '2017-10-9', '2017-11-11', '2017-12-25')
SET #MyCounter = #MyCounter + 1
SET #TempDate = DATEADD(d,1,#TempDate)
CONTINUE
END
PRINT #MyCounter
PRINT #TempDate
I am trying to get the correct year based on the current date and append to the fiscal year months date part but I am getting an error that it is not an integer. Ideas or thoughts?
`ALTER FUNCTION [dbo].[fn_CS_IssuedMODs] (#currentDate DATE)
RETURNS TABLE
AS
RETURN
(SELECT cs.Specialist, CASE WHEN COUNT(mn.mod_number_id) IS NULL
THEN 0 ELSE COUNT(mn.mod_number_id) END AS IssuedMODS,
cs.user_certificateSerialNumber
FROM dbo.tbl_modificationNumbers AS mn RIGHT OUTER JOIN
dbo.vw_ContractSpecialists AS cs ON mn.mod_specialist_id = cs.user_certificateSerialNumber
WHERE (mn.statusID = 10) AND effective_date between '10/1/'+DATEPART(YEAR,#currentDate)
+ CASE WHEN DATEPART(MONTH, #CurrentDate) >= 10 THEN -1 ELSE 0 END AND '09/30/'+DATEPART (YEAR,#currentDate)
GROUP BY cs.Specialist, cs.user_certificateSerialNumber`
lets create some variables for testing:
DECLARE #currentDate DATETIME = '4/2/2014'
DECLARE #FiscalYearStart DATETIME
DECLARE #FiscalYearEnd DATETIME
Now we are going to check whether the current date is before or after October 1 and if so, we are going to start the fiscal year using the previous year, otherwise we are in the new fiscal year.
SELECT #FiscalYearStart =
(
CASE
WHEN DATEPART(MONTH, #currentDate) < 10 THEN
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate) - 1, 0))
ELSE
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate), 0))
END
),
#FiscalYearEnd =
(
CASE
WHEN DATEPART(MONTH, #currentDate) < 10 THEN
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate), 0))
ELSE
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate) + 1, 0))
END
)
SELECT #FiscalYearStart As FiscalYearStart, #FiscalYearEnd As FiscalYearEnd
Output:
FiscalYearStart FiscalYearEnd
2013-10-01 00:00:00.000 2014-10-01 00:00:00.000
Now you can use effective_date >= #FiscalYearStart AND effective_date < #FiscalYearEnd in your query to pull the correct data for the year.
Instead of datepart() use either datename(). Or, do an explicit cast() to a string.
The + operator is overloaded in SQL Server. When it encounters a number, it is treated as addition -- and you can't add strings.
I can't figure out what your code is supposed to be doing. There are probably better ways to accomplish what you want. For starters, you should use ISO standard date formats (YYYYMMDD or YYYY-MM-DD) for constants.
Try this:
ALTER FUNCTION [dbo].[fn_CS_IssuedMODs] (#currentDate DATE)
RETURNS TABLE
AS
RETURN
(SELECT cs.Specialist, CASE WHEN COUNT(mn.mod_number_id) IS NULL
THEN 0 ELSE COUNT(mn.mod_number_id) END AS IssuedMODS,
cs.user_certificateSerialNumber
FROM dbo.tbl_modificationNumbers AS mn RIGHT OUTER JOIN
dbo.vw_ContractSpecialists AS cs ON mn.mod_specialist_id = cs.user_certificateSerialNumber
WHERE (mn.statusID = 10) AND effective_date between Cast(DATEPART(YEAR, DateAdd(Year, CASE WHEN DATEPART(MONTH, #CurrentDate) >= 10 THEN -1 ELSE 0 END, #currentDate)) as varchar) + '-10-1'
AND Cast(DATEPART(YEAR,#currentDate) as varchar) + '-09-30'
GROUP BY cs.Specialist, cs.user_certificateSerialNumber