question regarding oracle sysdate to last month date - database

OBSERVED_DATE BETWEEN add_months(trunc(sysdate,'mm'),-1) and last_day(add_months(trunc(sysdate,'mm'),-1))
I need to run query between the 01-11-2019 to 30-11-2019 how do I change the sysdate to last month values

Assuming your dates may have non-zero time of day, it is best to write such conditions with explicit inequalities, rather than using between. Between is inclusive of both ends, when what you need (in almost all cases) is >= 1 November and < 1 December.
If, instead, you write <= 30 November (which is what you do when you use between the way you are trying now), you will miss everything from the last day of the month, 30 November, that has a time-of-day greater than midnight.
The where clause should look like this:
...
where observed_date >= add_months(trunc(sysdate, 'mm'), -1)
and observed_date < trunc(sysdate, 'mm')
Still, if you insist on getting the last day of the previous month (such as 30 November) for whatever reason, that is simply
trunc(sysdate, 'mm') - 1
(that is, subtract one day from the first day of the current month).

There is another function which returns last day of the month. I.e. last_day
So your condition should look like this:
where observed_date
between add_months(trunc(sysdate, 'mm'), -1)
and last_day(add_months(sysdate, -1))
Cheers!!

Related

SQL Server : measuring real-time efficiency by operator

I've been working on some SQL code to measure efficiency in real-time for some production data. Here's a quick background:
Operators will enter in data for specific sub assemblies. This data looks something like this:
ID PO W/S Status Operator TotalTime Date
60129515_2000_6_S025 107294 S025 Completed A 38 05/08/2020
60129515_2000_7_S025 107294 S025 Completed A 46 05/08/2020
60129515_2000_8_S025 107294 S025 Completed A 55 05/08/2020
60129515_2025_6_S020 107295 S020 Completed B 58 05/08/2020
60129515_2025_7_S020 107295 S020 Completed B 47 05/08/2020
60129515_2025_8_S020 107295 S020 Completed B 45 05/08/2020
60129515_2000_1_S090 107294 S090 Completed C 33 05/08/2020
60129515_2000_2_S090 107294 S090 Completed C 34 05/08/2020
60129515_2000_3_S090 107294 S090 Completed C 21 05/08/2020
The relevant columns are the Operator, TotalTime and Date (note that the date is stored as varchar(50) because it plays nicer with Microsoft PowerApps that way).
What I need to do is:
Aggregate the sum of "TotalTime" grouped by Operator
Calculate the time elapsed based on a condition:
If between 7AM and 4PM, calculate the time elapsed since 7AM of the current day
If after 4PM, return the total time between 7AM and 4PM of the current day
Divide the SUM(TotalTime) by the TimeElapsed (AKA the first list item / second list item) in order to get a rough estimate of labor hours worked vs. hours passed in the day.
This calculation would change every time the query was ran. This will allow the Microsoft PowerApp that is pulling this query to refresh the efficiency measure in real time. I've taken a stab at it already - see below:
SELECT
md.Operator,
CASE
WHEN DATEADD(HOUR, -5, GETUTCDATE()) > CONVERT(DATETIME, CONVERT(DATE, DATEADD(HOUR, -5, GETUTCDATE()))) + '7:00' AND GETDATE() < CONVERT(DATETIME, CONVERT(DATE, DATEADD(HOUR, -5, GETUTCDATE()))) + '15:45'
THEN (SUM(isNull(md.TotalTime, 0)) + SUM(isNull(md.DelTime, 0))) * 1.0 / DATEDIFF(MINUTE, CONVERT(DATETIME, CONVERT(DATE, DATEADD(HOUR, -5, GETUTCDATE()))) + '7:00' , DATEADD(HOUR, -5, GETUTCDATE())) * 100.0
ELSE (SUM(isNull(md.TotalTime, 0)) + SUM(isNull(md.DelTime, 0))) / 420 * 100.0
END AS OpEfficiency
FROM
[Master Data] AS md
WHERE
md.[Date] = CONVERT(varchar(50), DATEADD(HOUR, -5, GETUTCDATE()), 101)
GROUP BY
md.Operator
Note: the DelTime is a different column regarding delay times. I am also converting back from UTC time to avoid any time zone issues when transferring to PowerApps.
However, this is horribly inefficient. I am assuming it is because the Date needs to be converted to datetime every single time. Would it work better if I had a calculated column that already had the date converted? Or is there a better way to calculate time elapsed since a certain time?
Thanks in advance.
There are a few things you can do to increase efficiency considerably. First, you want to make sure SQL can do a simple comparison when selecting rows, so you'll start by calculating a string to match your date on since your [Date] field is a string not a date.
Second, calculate the minutes in your shift (either 540 for a full shift or scaled down to 0 at 7 AM exactly) ahead of time so you aren't calculating minutes in each row.
Third, when summing for operators, use a simple sum on the minutes and calculate efficiency from that sum and your pre-calculated shift so far minutes.
One note - I'm casting the minutes-so-far as FLOAT in my example, maybe not the best type but it's clearer than other decimal types like DECIMAL(18,6) or whatever. Pick something that will show the scale you want.
My example uses a Common Table Expression to generate that date string and minutes-so-far FLOAT, that's nice because it fits in a direct query, view, function, or stored procedure, but you could DECLARE variables instead if you wanted to.
By filtering with an INNER JOIN on the [Date] string against the pre-calculated TargetDate string, I make sure the data set is pared down to the fewest records before doing any math on anything. You'll definitely want to INDEX [Date] to keep this fast as your table fills up.
All these together should give a pretty fast query, good luck
with cteNow as ( --Calculate once, up front - date as string, minutes elapsed as FLOAT (or any non-integer)
SELECT CASE WHEN 60*DATEPART(HOUR, GETUTCDATE())+DATEPART(MINUTE, GETUTCDATE()) > 60*21
--4PM in UTC-5, expressed in minutes
THEN CONVERT(float,(16-7)*60) --minutes in (4 PM-7 AM) * 60 minutes/hour
ELSE --Assume nobody is running this at 6 AM, so ELSE = between 7 and 4
CONVERT(float,60*DATEPART(HOUR, GETUTCDATE()) + DATEPART(MINUTE, GETUTCDATE()) - ((7+5)*60))
--Minutes since midnight minus minutes from midnight to 7 AM, shifted by
--UTS offset of 5 hours
END as MinutesToday --Minutes in today's shift so far
, FORMAT(DATEADD(HOUR,-5,GETUTCDATE()),'MM/dd/yyyy') as TargetDate --Date to search for
--as a string so no conversion in every row comparison. Also, index [Date] column
)
SELECT md.Operator, SUM(md.TotalTime) as TotalTime, SUM(md.TotalTime) / MinutesToday as Efficiency
FROM [Master Data] AS md INNER JOIN cteNow as N on N.TargetDate = md.[Date]
GROUP BY md.Operator, MinutesToday
BTW, you didn't make allowances for lunch or running before 7 AM, so I also ignored those. I think both could be addressed in cteNOW without adding much complexity.

Find startdate of a week from last year in SSRS

We assume that the week starts from Mon and ends at Sunday.
While trying to comparing with last year, how can we get the right dates?
For example, if i want to compare this week (10/23/2017 to 10/29/2017) with year same week (10/24/2016 to 10/30/2016)
I am setting 2 parameters in SSRS BeginDate and BeginDateLastYear . The value of BeginDate = 10/23/2017
How can i assume the value for BeginDateLastYear as a value precisely to the Monday of same week number in last year ( IN this case it should be 10/24/2016) ?
Currently i am trying to set the value of StartDate in last year like
BeginDateLastYear =DATEADD(DateInterval.Year,-1,Parameters!BeginDate.Value)
Also i tried to use
=DATEADD(DateInterval.Week,-52,Parameters!BeginDate.Value) , but not very sure will this work precisely to same week start date of last year
In reporting services you can use the following expression for BeginDateLastYear
= DATEADD(
DateInterval.WeekOfYear,
IIF(
DATEPART(DateInterval.WeekOfYear,DATEADD(DateInterval.WeekOfYear, -52 , Parameters!BeginDate.Value))<> DATEPART(DateInterval.WeekOfYear, Parameters!BeginDate.Value) ,
-53,
-52
) ,
Parameters!BeginDate.Value
)
Code logic:
If by going back 52 weeks the two week numbers do not match go back 53 weeks, else go back 52 weeks
In case you want to implement it in SQL without using the second parameter (SSRS has some issues on cascading parameters)
SET #BeginDateLastYear =
DATEADD(
wk,
CASE WHEN DATEPART(wk,DATEADD(wk, -52 , #BeginDate))<> DATEPART(wk,#BeginDate)
THEN -53
ELSE -52 END,
#BeginDate
)

Is Date between 1st of 1st Month and 29th of 7th month of any year (Sql Server 2008+)

I need to check if a Date is between 1st of 1st month and 29th of 7th month of any year.
Before going ahead and hacking extracting Month Day from the date and comparing it to being between 0101 and 0729 (or just checking if the date dd mm is less than 629) type of hideousity solution, I must ask is there a function to check wether any date falls in between particalr days and month , invariant of the it's year.
Other wise extracting months and days from the date and doing some hacky arithmatic is easy, but I want to be leave a better code for the poor programmer who will come after me and not having to guess why the hell some freaky made up arithmatic is going on, I rather be explicit even if it takes longer.
Another option:
DECLARE #TheDate date = '2016-07-29';
SELECT 1
WHERE DATEADD(Year, -(YEAR(#TheDate) - 2000), #TheDate)
BETWEEN '2000-01-01' AND '2000-07-29'
where datetimeField < dateFromParts( year( dateTimeField ), 7, 30)
EDIT: Above would work in 2012 and later. For 2008 you could do some arithmetic which is "very simple" for a developer:
where datetimeField < CAST(CAST(year(dateTimeField) AS CHAR(4))+'0730' AS DATE);
Or simplified a bit:
where datetimeField < CAST(year(dateTimeField) AS CHAR(4))+'0730';
PS: A common pitfall in using BETWEEN (which acts like x >= v1 and x <= v2) on a datetime field, where the field might have time part, is not suggested and would never help to precisely get the correct records (because there is no way to specify the ending time). Instead you should always use x >= v1 and x < v2 style where v2 is the minimum exclusive upper value. ie: To get all the sales in May 2000 (saleDate has time component):
saleDate >= '20000501' and saleDate < '20000601'
NOT:
saleDate between '20000501' and '20000531'
OR NOT:
saleDate >= '20000501' and saleDate <= '20000531'
Select * from TableWithNoName where DateField1 between DATETIMEFROMPARTS( YEAR(DateField1),1, 1, 0, 0, 0, 0 ) and DATETIMEFROMPARTS( YEAR(DateField1),7, 29, 23, 59, 59, 999 )
assuming DateField1 is a Date Value

SQL Server 2008 - datediff month

I have the following query below --
SELECT *
FROM
dbo.patient AS p
INNER JOIN
dbo.study AS s ON s.patient_fk = p.pk
/***where p.pk = s.patient_fk***/
WHERE
s.study_custom1 LIKE'%hosp%'
AND s.study_datetime >= DATEADD(month, datediff(month, 0, getdate()) -1, 0)
AND s.study_datetime < DATEADD(month, datediff(month, -1, getdate()) -1, 1)
I want to find all dates for the most recent prior month. In this case it is the month of July, but the query is picking up '8/1/2014' along with July dates.
How do I need to modify this to exclude August and only find July?
Thanks
Try this
select *
from dbo.patient AS p
INNER JOIN dbo.study AS s
on s.patient_fk = p.pk
/***where p.pk = s.patient_fk***/
where s.study_custom1 like '%hosp%'
and DATEPART(year,s.study_datetime) = DATEPART(year,DATEADD(month,-1,GETDATE()))
and DATEPART(month,s.study_datetime) = DATEPART(month,DATEADD(month,-1,GETDATE()))
EDIT: Note: You could speed execution by setting variables instead of computing the month and year of today for every row, but as you had your original in a query without variables I provided a less computationally efficient solution for the sake of making sure you could use it in your application.
Also note that the arithmetic for getting the year and month of "last month" must be done inside the DATEPART so the query will function correctly in January.
EDIT 2: Explaining optimization
My first edit was written before I saw your comment, so I'm not sure if you're asking for an explanation of my original query, or asking for an explanation of my suggested optimization, but I'll guess the optimization, I'll be happy to further explain the query if that's what you meant.
So the weakness of my first solution is it makes 8 function calls per row. Not a big deal, but it could add up for a large database. If you instead define variables that you populate outside the query, you can reduce it to a pair of index scans per row, which is significantly faster. Significantly being maybe 1/10 of a second if you only have thousands of patients :-)
But regardless, here is a more efficient solution.
DECLARE #StartM DATE
DECLARE #EndM DATE
SET #StartM = DATEADD(month,-1,CONVERT(DATE,SUBSTRING(CONVERT(VARCHAR(50),GETDATE(),20),1,8)+'01'))
SET #EndM = DATEADD(month,1,#StartM)
select *
from dbo.patient AS p
INNER JOIN dbo.study AS s
on s.patient_fk = p.pk
/***where p.pk = s.patient_fk***/
where s.study_custom1 like '%hosp%'
and s.study_datetime >=#StartM
and s.study_datetime < #EndM
Note that #EndM will be the midnight of the first day of this month, so even if your s.study_datetime is 11:59 PM on the last day of last month it still gets included. If you build an index on s.study_datetime, this will be very fast.

How can I find out all the dates of a given day in this month?

I have to write a program in C which, given the name of a day, returns all of the dates in this month that the day will be on.
For example, if the input is "Sunday" the output should be: 5,12,19,26 (which are the days Sunday will be on this month.)
Does any one have any idea how to do this? I have tried a lot.
You can use the time() function to get the current time.
Then use the localtime() function to get a struct (struct tm) with the information (year, month, day, ...) of the current time.
From the "struct tm" get the tm_mday and the tm_wday. Use these fields to determine the next or previous sunday. e.g. if tm_mday is 12, and tm_wday is 3 (wednesday), then we now that the 9th of this month is a sunday (12-3 = 9). From that number simply add or subtract 7 to get all other sundays.
You need to know it for a given year too? Or is this for only this year? If you need to know it for any given year, you can do a "days per month" enum, having one for the leap years and one for the non-leap years.
You just need to know in which day of week started a year (i.e: "Monday", "Tuesday", etc)
You will, at least, have 5 dates for any given month, so, you can have a fixed length array of ints.
You know that the gregorian calendar repeats itself each 400 years, and that if a year X started with day "Y", then, year X + 1 will start with day ("Y" + 1) % 7 if x is not a leap year, if it is a leap year, it will start with day ("Y" + 2).
that could give you the first date of any year, and knowing how many days have all the months for any given year, you can easily get what date that month starts in ("Monday", etc).
Then, all you have to do, is something like
int offset = 0;
int i;
while (myDate + offset != monthStartingDate) {
offset++;
}
i = offset + monthStartingDate;
(myDate is the number of day of week, and monthStartingDate is the number of day of week for the first day of that month)
when you go out of that loop, you will have the first ocurrence, then, you just add 7 until i is out of month bounds.
you can add each i into the array..
int res[5] = {0,0,0,0,0}
for ( ; i < daysOfMonth(month, year); i += 7) {
int res[i / 7] = i;
}
then you just return res.
Oh, I dind't know that you were able to use date functions :P I think the idea of the excercise was practicing C :P
1) Take a string (weekday name) from the input (use scanf or gets)
2) Convert it to a number (find it's index in a table of weekdays using loop and strcmp), assign 0 to Sunday, 1 for Monday ...
3) Get current time with time function and convert it to tm struct with localtime function
4) From tm struct calculate the first day in current month of a given weekday
first_mday_of_given_wday = (tm_struct.tm_mday + given_wday - tm_struct.tm_wday + 6) % 7 + 1
5) Find out how many days is in current month. To do this:
put 1 into tm_mday and 0 into tm_isdst of your tm struct
duplicate the struct
increase by 1 tm_mon in the duplicate (watch for the last month! in that case increase tm_year by 1 and set tm_mon to 1)
convert booth strutcs to time_t with mktime function and calculate difference (just subtract these time_t values), convert result from seconds to days (divide by 60*60*24)
6) Run a loop though calculated range:
for (i = first_mday_of_given_wday; i <= num_days_in_month; i += 7) printf("%d, ", i)
5th step can be omitted in a certain situations. We know that a month can have from 28 to 31 days. If any of hypothetical days 29,30,31 of current month cannot be a given weekday we can assume that current month has 28 days.
So simply - assume we have 28 days in current month if first_mday_of_given_wday is more then 3, otherwise calculate the number like shown in 5th step.

Resources