Run duration sysjobs - sysjobshistory - sql-server

I have a question regarding the run_duration of sysjobs. Official docs seem to contradict itself:
https://learn.microsoft.com/en-us/sql/relational-databases/system-tables/dbo-sysjobhistory-transact-sql?view=sql-server-2017
First it specifies:
run_duration int Elapsed time in the execution of the job or step in HHMMSS format.
But then it also mentiones a query for a more friendly time format:
SELECT sj.name,
sh.run_date,
sh.step_name,
STUFF(STUFF(RIGHT(REPLICATE('0', 6) + CAST(sh.run_time as varchar(6)), 6), 3, 0, ':'), 6, 0, ':') 'run_time',
STUFF(STUFF(STUFF(RIGHT(REPLICATE('0', 8) + CAST(sh.run_duration as varchar(8)), 8), 3, 0, ':'), 6, 0, ':'), 9, 0, ':') 'run_duration (DD:HH:MM:SS) '
FROM msdb.dbo.sysjobs sj
JOIN msdb.dbo.sysjobhistory sh
ON sj.job_id = sh.job_id
Notice the difference, days are 'suddenly' brought into the picture.
In my real life example, I came across a job that ran really long. The results are as follows:
run_duration (DD:HH:MM:SS) run_duration
01:49:39:39 1493939
So how do I read this? Is this actually 149 hours, 39 minutes and 39 secs?
One day and 49 makes no sense.
Thanks a lot for the feedback!

Normally, if you're using a notation with times, the largest denominator isn't limited; as in that you stop at 24 for hours because that's how many there are in a day. So, in the format HHMMSS, HH can be any value from 0+. the HH isn't limited to 24, as you aren't using a days denominator. Just like if you were counting months you wouldn't stop as 12, even if the difference between 2 dates is 16 months. You're counting in months so why would crossing a year gap stop you.
Like you commented as well 1 day 49 hours literally makes no sense. 1493939 should be read as 149 hours, 39 minutes and 39 seconds.

Related

Cumulative sum of value with arrayformula and reset if =>20 - Google sheets [duplicate]

Please provide an array formula. Can you help to reset running totals when MOQ is reached?. Here MOQ=15. When running total becomes equal to or greater than 15 it should restart.
Date
Value
Desired
12/2022
6
6
01/2023
5
11
02/2023
4
15
03/2023
3
3
04/2023
9
12
05/2023
2
14
06/2023
6
20
07/2023
1
1
08/2023
6
7
09/2023
1
8
10/2023
8
16
11/2023
9
9
12/2023
3
12
all we need is a unique common ID for grouping the sum. we start with months to fall from whatever date to months 3, 7 and 11:
=ARRAYFORMULA(IFNA(VLOOKUP(MONTH(A2:A), {3;7;11}, 1, 1), 11))
next, we can use years to differentiate between 11/2022 and 11/2023 so we take whatever date and convert it into first day of given month and then offset the year by 58 days:
=ARRAYFORMULA(YEAR(EOMONTH(A2:A, -1)+1-58))
we combine it to get a unique ID per MOQ:
=ARRAYFORMULA(IFERROR(IFNA(VLOOKUP(MONTH(A2:A), {3;7;11}, 1, 1), 11)&
" "&YEAR(EOMONTH(A2:A, -1)+1-58)))
then we just use standard running total fx:
=ARRAYFORMULA(IF(A2:A="",,
MMULT(--TRANSPOSE(IF((TRANSPOSE(ROW(A2:A))>=ROW(A2:A))*(
IFERROR(IFNA(VLOOKUP(MONTH(A2:A), {3;7;11}, 1, 1), 11)&"×"&
YEAR(EOMONTH(A2:A, -1)+1-58))=TRANSPOSE(
IFERROR(IFNA(VLOOKUP(MONTH(A2:A), {3;7;11}, 1, 1), 11)&"×"&
YEAR(EOMONTH(A2:A, -1)+1-58)))), B2:B, 0)), ROW(A2:A)^0)))
update:
=ARRAYFORMULA(IF(A2:A="",, MMULT(--TRANSPOSE(IF((TRANSPOSE(ROW(B2:B))>=ROW(B2:B))*(
ARRAY_CONSTRAIN({0; IF(TRUNC(SUMIF(ROW(B2:B), "<="&ROW(B2:B), B2:B)/15)>
MAX(TRUNC(SUMIF(ROW(B2:B), "<="&ROW(B2:B), B2:B)/15))-1,
MAX(TRUNC(SUMIF(ROW(B2:B), "<="&ROW(B2:B), B2:B)/15))-1,
TRUNC(SUMIF(ROW(B2:B), "<="&ROW(B2:B), B2:B)/15))}, ROWS(B2:B), 1)=TRANSPOSE(
ARRAY_CONSTRAIN({0; IF(TRUNC(SUMIF(ROW(B2:B), "<="&ROW(B2:B), B2:B)/15)>
MAX(TRUNC(SUMIF(ROW(B2:B), "<="&ROW(B2:B), B2:B)/15))-1,
MAX(TRUNC(SUMIF(ROW(B2:B), "<="&ROW(B2:B), B2:B)/15))-1,
TRUNC(SUMIF(ROW(B2:B), "<="&ROW(B2:B), B2:B)/15))}, ROWS(B2:B), 1))), B2:B, 0)),
ROW(B2:B)^0)))
Use the new SCAN function
=SCAN(0,B2:B, LAMBDA(a,c, IF(c="",,IF(a>=15,c,a+c))))
What we did is: return the running total of Values if less than or equal 15, else rest.
Explation
SCAN Takes initial_value we set it to 0 and array_or_range we name it c "Current value" for short, and the accumulator in lambda a you can name them any thing.
IF the accumulator a is greater or equal 15 >= 15 then reset by returning the Current value of array_or_range c, if not add the current value of array_or_range c to the accumulator a.
Used formulas help
SCAN - LAMBDA - IF

SSRS. How to group in a group?

I have SSRS report like below with Boolean parameter to show 12h view or 24h view. To fit report into single screen the 24h report need to group by every 2hr.
07:00 08:00 09:00 10:00 11:00 12:00 13:00 14:00 ...
Line 1 25 30 24 26 25 25 30 30 ...
08:00 10:00 12:00 14:00 ...
Line 1 55 50 50 60 ...
The query for the dataset is:
SELECT LineID
,Hour
,HourValue
,Target
FROM vwData
ORDER BY LineID, CASE WHEN [Hour] > 6 THEN - 1 ELSE [Hour] END
How can I achieve this?
This declares your bit variable (which should be true when they want the 24 hour view - false when 12 hour)
DECLARE #24Hour bit = 0
SELECT CASE WHEN #24Hour = 0
THEN Hour
ELSE Hour + (Hour % 2)
END AS [HourGroup]
,SUM(Target) AS [TargetTotal]
FROM vwData
GROUP BY CASE WHEN #24Hour = 0
THEN Hour
ELSE Hour + (Hour % 2)
END
If they want the 24 hour view, we make hour = hour + hour % 2. (7 = 8, 8=8, 9=10, etc., etc.). If you had a more complex query, I would suggest reading up on cross apply, but this is so simple I think this will suffice. The grouping by makes sure to aggregate the REAL 7 and REAL 8 hour records (which will both be returned as "8", if using the 24 hour view). If you don't group your results, you will get two 8 oclock records - one with the REAL 7 hour total and one with the REAL 8 hour total.
EDIT:
Since you didn't include the schema of your DB, I'm guessing that 'Target' is the value being summated, but it could just as easily be 'HourValue'. Furthermore, I have no idea why you would need LineID, so I omitted it from my answer. But you can easily modify that if it's inaccurate. In the future, you should provide some sample data and your database schema so that others aren't forced to make assumptions or guess.
You could add a calculated field with a value given by something like this: `Fields!Hour.Value + Fields!Hour.Value Mod 2' and then group on that field, using a parameter to choose the Group By field in the report (Your new field or the actual hour value).

Get week number of month SQL Server

I am trying to find the week number of a particular month given a date, so I want to know which week is that for given month
Example if I enter
2016 Feb 2 ---> Week 1
2016 Feb 9 ---> Week 2
2016 June 2 ---> week 1
2016 Jan 25 ---> week 5
Can I achieve this in a T-SQL query?
I have seen the following option
DATEPART(wk, BookingTimeStamp)
But that gives the week number of the year, not the month
The idea is to build result per week for a given month
The first result using "SQL Server and Weeks in Month" returned this article. It shows two ways using DATEPART along with other date parsing functions. Here is one solution:
DECLARE #MyDate DATETIME =GETDATE()
SELECT DATEDIFF(WEEK, DATEADD(MONTH, DATEDIFF(MONTH, 0, #MyDate), 0), #MyDate) +1
I ran into the same problem when I wanted to get not the number of the week in the year, but the number of the week in relation to the month.
In my solution, you can divide every day of date by 7. Then all the days of week 1 will be between 0 and 1. All days of week 2 are between 1 and 2, and so on.
This is a simple example of a practice query where you can use the case construct to find the number of the week.
Solution 1 (a bit confusing)
SELECT
case
when CAST(strftime('%d', date) as real) / 7 BETWEEN 0 AND 1 then 'week_1'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 1 AND 2 then 'week_2'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 2 AND 3 then 'week_3'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 3 AND 4 then 'week_4'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 4 AND 5 then 'a_bit_of_week_5'
end as week
FROM cost
WHERE strftime('%Y', date) = '2022';
Solution 2 (simple)
SELECT
case
when CAST(strftime('%d', date) as integer) BETWEEN 1 AND 7 then 'week_1'
when CAST(strftime('%d', date) as integer) BETWEEN 7 AND 14 then 'week_2'
when CAST(strftime('%d', date) as integer) BETWEEN 14 AND 21 then 'week_3'
when CAST(strftime('%d', date) as integer) BETWEEN 21 AND 28 then 'week_4'
when CAST(strftime('%d', date) as integer) BETWEEN 28 AND 31 then 'a_bit_of_week_5'
end as week
FROM cost
WHERE strftime('%Y', date) = '2022';
Deduction of solution 1
If you're just looking for a solution to an issue, then you don't need to read any further. I just want to tell you how I arrived at this solution and why you can trust it.
This solution can be visualized using the Python programming language.
from matplotlib import pyplot as plt
days = [x for x in range(1, 32)]
days_on_seven = [x / 7 for x in range(1, 32)] # Divide each day by 7
print(days_on_seven)
## Result is below
[0.14285714285714285,
0.2857142857142857,
0.42857142857142855,
0.5714285714285714,
0.7142857142857143,
0.8571428571428571,
1.0,
1.1428571428571428,
1.2857142857142858,
1.4285714285714286,
1.5714285714285714,
1.7142857142857142,
1.8571428571428572,
2.0,
2.142857142857143,
2.2857142857142856,
2.4285714285714284,
2.5714285714285716,
2.7142857142857144,
2.857142857142857,
3.0,
3.142857142857143,
3.2857142857142856,
3.4285714285714284,
3.5714285714285716,
...
3.857142857142857,
4.0,
4.142857142857143,
4.285714285714286,
4.428571428571429]
Visualisation
As you can see, we get an array of increasing numbers. Let's graph the resulting numbers for each day of the month.
from matplotlib import pyplot as plt
import seaborn as sns
sns.lineplot(x=days, y=days_on_seven)
plt.xlabel("Number of day in month")
plt.ylabel("Result of division by 7")
plt.title("day / 7 plot")
The graph below by link
https://i.stack.imgur.com/SIVfT.png
The graph is very crude, but you can see, as noted earlier, that all the days of week 1 will be between 0 and 1. All days of week 2 are between 1 and 2, and so on.
I hope my decision was helpful and interesting to you.

Adjusting dates to avoid overlap of days

If I have three dates, e.g. Jan 1, Jan 25, and Feb 20 but I want the dates to be separated by 30 days, how can i do it?
For example, what I want to do is Jan 1, Jan 30, Feb 29.
I am very new at R but the code should be something like this - If 2nd date is before (1st date+30), then adjust 2nd date to (1st+31) and similarly for 3rd date..
Any help will be much appreciated!
Since you want a fixed distance between each adjacent pair of dates, you don't need to "adjust" any dates; rather, you can just compute the desired date vector from scratch, starting with the first date.
This can actually be done with a single call to the S3 generic seq(), which will dispatch to seq.Date():
seq(as.Date('2000-01-01'),by=30,length.out=3);
## [1] "2000-01-01" "2000-01-31" "2000-03-01"
Also note that you seem to have made an error in deriving your expected dates; 30 days from Jan 1 is Jan 31, not Jan 30.
d1 = as.Date("01-01",format="%m-%d")
d2 = as.Date("01-25",format="%m-%d")
if (abs(as.numeric(difftime(d2,d1)))<30) d2 = d1 + 30
>d2
[1] "2015-01-31"

Correctness of Sakamoto's algorithm to find the day of week

I am using Sakamoto's algorithm to find out the day of week from a given date.
Can anybody tell me the correctness of this algorithm? I just want this from 2000 to 2099.
The algorithm from Wikipedia is given for reference.
int dow(int y, int m, int d)
{
static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
y -= m < 3;
return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}
Well, you can tell just by looking at it that it is correct... Assuming that the t[] array is correct, which you can verify with just 12 spot checks (one for each month using any day/year).
The y -= m < 3 is a nice trick. It creates a "virtual year" that starts on March 1 and ends on February 28 (or 29), putting the extra day (if any) at the end of the year; or rather, at the end of the previous year. So for example, virtual year 2011 began on Mar 1 and will end on February 29, while virtual year 2012 will begin on March 1 and end on the following February 28.
By putting the added day for leap years at the end of the virtual year, the rest of the expression is massively simplified.
Let's look at the sum:
(y + y/4 - y/100 + y/400 + t[m-1] + d) % 7
There are 365 days in a normal year. That is 52 weeks plus 1 day. So the day of the week shifts by one day per year, in general. That is what the y term is contributing; it adds one to the day for each year.
But every four years is a leap year. Those contribute an extra day every four years. Thanks to the use of virtual years, we can just add y/4 to the sum to count how many leap days happen in y years. (Note that this formula assumes integer division rounds down.)
But that is not quite right, because every 100 years is not a leap year. So we have to subtract off y/100.
Except that every 400 years is a leap year again. So we have to add y/400.
Finally we just add the day of the month d and an offset from a table that depends on the month (because the month boundaries within the year are fairly arbitrary).
Then take the whole thing mod 7 since that is how long a week is.
(If weeks were eight days, for example, what would change in this formula? Well, it would be mod 8, obviously. Also the y would need to be 5*y, because 365 % 8 == 5. Also the month table t[] would need adjusting. That's it.)
Incidentally, Wikipedia's statement that the calendar is "good until 9999" is totally arbitrary. This formula is good for however long we stick with the Gregorian calendar, whether that is 10 years, 100 years, 1000 years, or 1 million years.
[edit]
The above argument is essentially a proof by induction. That is, assuming that the formula works for a particular (y,m,d), you prove that it works for (y+1,m,d) and (y,m,d+1). (Where y is a "virtual year" starting March 1.) So the key question is, does the sum change by the correct amount as you move from one year to the next? With knowledge of the leap year rules, and with the "virtual year" having the extra day at year end, it trivially does.
Recently I wrote blog post about this algorithm here.
The basic idea behind algorithm is to for February and January to count day
of week from 31 Dec of the previous year. For all other months we will be counting day of week from current year 31 Dec. We do this in two
steps first we compute day of week of last day of month preceding current month m then we just add d modulo seven.
31 Dec 1 BC is Sunday that is encoded as 0, Monday is 1 etc.
So we have: 0 + y + y/4 - y/100 + y/400 this with y -= m < 3 computes
day of week of 31 Dec of current year or previous year (depending on month). Note: 365 % 7 == 1 this explains why we wrote y instead of 365*y. The last component d is obvious since we start counting day of week from previous month last day.
The last part that need to be explained are values in array, for first two values these are number of days since last year 31 Dec to start of the month % 7. For rest of the months they are negated modulo seven number of days from end of prev month to 31 Dec of the current year. In other words we are subtracting days by addition modulo 7 e.g. (a-b)%7 = (a+(7-b%7))%7.
More explanation you may find in my blog post.
This might not be a complete answer like some mentioned above, But just would like to add one thing regarding this array : 0 3 2 5 0 3 5 1 4 6 2 4
Consider months beginning from March and ending at February just like others said:
March
April
May
June
July
August
September
October
November
December
January
February
Writing January to December from above numbering style :
So consider this as an array :
int t[] = {11,12,1,2,3,4,5,6,7,8,9,10};
Now for all elements in array just do : (2.6*m - 0.2) mod 7
parse the result as integer and you will get this:
0 3 2 5 0 3 5 1 4 6 2 4
You can find this formula here : wikipedia
int dayOfWeek(int d, int m, int y){
// Months Array
int t[] = {11,12,1,2,3,4,5,6,7,8,9,10};
// Convert months array
for (int i = 0; i < 12; i++){
int ans = t[i] * 2.6 - 0.2;
t[i] = ans % 7;
}
// Continue Algo
if(m<3)
y -= 1;
int day = (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
return day;
}
this : + y/4 - y/100 + y/400 is related to leap year. The algo to check for leap year is :
Perfectly Divisible by 400 -> true
IF perfectly divisible by 100 but not by 400 -> False
Divisible by 4 -> True
perform checks on above order. Maybe that is why they subtracted y/100 and added y/4 & y/400. Yeah silly logic 😅
I know this might not be the answer, But this might help to those who find it difficult to remember/understand stuff, yeah! not all of us have high IQ levels of understanding stuff and sadly some of us can't remember stuff too, lol.
For Gregorian Calendar
int dayToWeekG(int d,int m,int y){
int i;
int t[12]={0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
//{0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
y-=m<3;
i=(y+y/4-y/100+y/400 +t[m-1]+d)%7;
return i;
}
Explanation:
See the commented array for
t[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
and compare it with a calendar of a whole year (run cal 2to generate calendar in terminal in linux/unix) notice the starting day of the week of the day for each month.
Every normal year shifting one day of the week and leap year shifting two days of the week. as (365%7)=1 and (366%7)=2
i= y+y/4-y/100+y/400
But we are should not calculate the extra day if y is a leap year for month 0 and 1
y-=m<3
but by this way we are also removing the extra day from non-leap years too. so we will fill up the gap by subtracting 1 day for every month after February.
int t[12]={0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};

Resources