Decimal to time SQL Server - sql

I have two decimals that are represented as time in SQL Server:
[Driver_Hours_QT]
[Driver_Minutes_QT]
Result:
Driver_Hours_QT = 202.00000
Driver_Minutes_QT = 41.00000
I would like to concat these to get one time represented as 202.41 in order to divide by # of bills (394) to get a bill per hour.
How would I do this?

Based on your examples, you want arithmetic:
Driver_Hours_QT + Driver_Minutes_QT / 100.0

For bills per hour - you want to divide by the time
declare #hours as decimal
set #hours = 202
declare #mins as decimal
set #mins = 41
declare #bills as integer
set #bills =394
select #Bills/(#hours+#mins/60)
Obviously if you want Time per bill, then it's
select (#hours+#mins/60)/#Bills

Related

Converting a time excel formula to t-sql

So I'm new to SQL (I believe it's T-SQL) and I'm trying to convert a function I used in Excel to SQL.
L2 becomes Column 1
G2 becomes Column 2
=(INT(L2)-INT(G2))*("17:00"-"08:45")+MEDIAN(MOD(L2,1),"17:00","08:45")-MEDIAN(MOD(G2,1),"17:00","08:45")
What this does is calculate the business hours worked between 8:45AM and 05:00PM.
If work goes from 4:00PM to 9:00AM the next day, the result should be 01:15:00.
If it goes over several days (4:00PM on the 1st to 9:00AM on the 4th) it should be 17:45:00.
I'd prefer not to have a separate function because I don't know how to use them as I'm quite new to this - I'd prefer to have it as something I can write within the SELECT * , <code here here> FROM db.name section.
Thanks in advance
I know you said you don't want this in a function, but they really aren't hard to use and the logic you require for this is too complex in SQL Server to be sensibly contained inline (Though it can be, if you really want to be that guy).
This function has no error handling if any of your parameters are not suitable, though I will leave that up to you as a learning exercise on NULL values, process flows and fully thinking through all the possibilities that you may need to deal with:
-- This bit creates your function. You can rename the function from fnWorkingDays to anything you want, though try to keep your naming conventions sensible:
create function fnWorkingDays(#Start datetime
,#End datetime
)
returns decimal(10,2)
as
begin
-- Declare the start and end times of your working day:
declare #WorkingStart time = '08:45:00.000'
declare #WorkingEnd time = '17:00:00.000'
-- Work out the number of minutes outside the working day in 24 Hour Notation:
declare #OvernightMinutes int = datediff(minute -- Work out the difference in minutes,
,cast(#workingend as datetime) -- between the end of the working day (CASTing a TIME as DATETIME gives you 1900-01-01 17:00:00)
,dateadd(d,1,cast(#WorkingStart as datetime)) -- and the start of the next working day (CAST the TIME value as DATETIME [1900-01-01 08:45:00] and then add a day to it [1900-01-02 08:45:00])
)
-- There is no need to retain the minutes that fall outside your Working Day, to if the very start or very end of your given period fall outside your Working Day, discard those minutes:
declare #TrueStart datetime = (select case when cast(#Start as time) < #WorkingStart
then dateadd(d,datediff(d,0,#Start),0) + cast(#WorkingStart as datetime)
else #Start
end
)
declare #TrueEnd datetime = (select case when cast(#End as time) > #WorkingEnd
then dateadd(d,datediff(d,0,#End),0) + cast(#WorkingEnd as datetime)
else #End
end
)
-- You can now calculate the number of minutes in your true working period, and then subtract the total overnight periods in minutes to get your final value.
-- So firstly, if your Working Period is not long enough to stretch over two days, there is not need to do any more than calculate the difference between the True Start and End:
return (select case when datediff(minute,#Start,#End) < #OvernightMinutes
then datediff(minute,#TrueStart,#TrueEnd)
-- If you do need to calculate over more than one day, calculate the total minutes between your True Start and End, then subtract the number of Overnight Minutes multiplied by the number of nights.
-- This works because DATEDIFF calculated the number of boundaries crossed, so when using DAYS, it actually counts the number of midnights between your two dates:
else (datediff(minute,#TrueStart,#TrueEnd) - (datediff(d,#TrueStart,#TrueEnd) * #OvernightMinutes))/1440.
-- If you want to return your value in a slightly different format, you could use variations of these two, though you will need to change the RETURNS DECIMAL(10,2) at the top to RETURNS NVARCHAR(25) if you use the last one:
-- else datediff(minute,#TrueStart,#TrueEnd) - (datediff(d,#TrueStart,#TrueEnd) * #OvernightMinutes)
-- else cast((datediff(minute,#TrueStart,#TrueEnd) - (datediff(d,#TrueStart,#TrueEnd) * #OvernightMinutes))/60 as nvarchar(5)) + ' Hours ' + cast((datediff(minute,#TrueStart,#TrueEnd) - (datediff(d,#TrueStart,#TrueEnd) * #OvernightMinutes))%60 as nvarchar(5)) + ' Minutes'
end
)
end
go
And this is how you call the function:
select dbo.fnWorkingDays('2016-09-04 12:00:00.000', '2016-09-06 12:10:00.000') as WorkingDays
You can replace the two DATETIME values about with the appropriate column names to get your desired result inline:
select dbo.fnWorkingDays(Dates.StartDate, Dates.EndDate) as WorkingDays
from (select '2016-09-04 12:00:00.000' as StartDate
,'2016-09-06 12:10:00.000' as EndDate
) as Dates

SQL finding average between dates

I have the user write in two dates, say: '2013-7-8','2013-7-15'
The stored procedure then executes and returns and updated table.
Two columns include:
Avg_Paid_Volume ---- cns_amt
numb_im_looking_for -------------------------10000
numb_im_looking_for ------------------------ 20000
numb_im_looking_for -------------------------30000
... etc
cns_amt is just volume over the specified time period.
I'm looking for the average paid volume across between the specified two dates, which i called #dt_from and #dt_to -> in this case it would be 10000/8, 20000/8 and 30000/8
I would like to have those values inputed into my table (into the 'numb_im_looking_for')
Declare #dt_to datetime
Declare #dt_from datetime
set #dt_to = convert(datetime,'2013-7-8')
set #dt_from = convert(datetime,'2013-7-15')
Update tableName
Set avg_paid_volume = (cns_amt / datediff(d,#dt_to,#dt_from))
datediff is key here

Calculating run down dates using only percentages using Microsoft SQL Server

I have a table with information on resources, basics are:
ID Total Start End Used
----------------------------------------------
1 350 01-01-2012 31-12-2012 80.6%
2 250 01-01-2012 31-12-2012 51.5%
3 3500 01-01-2012 31-07-2013 12.5%
4 350 01-01-2012 31-10-2012 91.0%
Columns are:
Total -- Total number of the resource (Being currency, time, paper etc).
Start -- Start date of resource
End -- End date of resource
Used -- Percentage of the resource used to date
I have to try to work out (or estimate) when the resource will run out at the rate used so far.
I've tried several different ways using the percentage used and the percentage but nothing makes sense, I'm pretty sure there's a simple way to do this that's staring me in the face but I can't find it.
My ideal output would be in text below but I will probably format in the application:
You have used X% of your [resource name] in Y% of the time allotted,
at this rate the resource will run down around [Run Down Date].
Can anyone work out how this can be calculated?
SQL Fiddle to play with
Edit:
To try and make the problem clearer, I'll explain how I would calculate a single date:
For the first line (ID = 1).
Average % per day = Percentage (80.6) / Days between Start and Today (205)
Average % per day = 0.003931707%
% remaining = Percentage (80.6%)
% remaining = 19.4%
Days remaining = Average % per day (0.003931707%) / % remaining (19.4%)
Days remaining = 49.34243176
Project Run Down = Today + Days Remaining (49.34243176)
Project Run Down = 11/09/2012 (11th Sep)
I've tried converting this process into SQL but I can't get it to work.
you can try something like this
declare #res table(
id int identity(1,1)
,total int
,start date
,[end] date
,used float )
insert into #res(total, start , [end], used)
values
(350, '20120101', '20121231', 0.806)
,(250, '20120101', '20121231', 0.515)
select
*
,used/DATEDIFF(DAY,start,GETDATE()) as avUsePerDay
,1/(used/DATEDIFF(DAY,start,GETDATE())) as expectedDaysTotal
,DATEADD(day,1/(used/DATEDIFF(DAY,start,GETDATE())),start) as expectedToDie
from #res
That would be...
SELECT DATEDIFF(d, [StartDate], GETDATE()) * 100.0 / DATEDIFF(d, [StartDate], [EndDate]) AS PercentageTimeGone,
DATEADD(d, (100 - USED) / (Used / DATEDIFF(d, [StartDate], GETDATE())), GETDATE()) AS ProjectedEndingDate
FROM Resources
What's about
select id, total, start, end, datediff(day,start,getdate()) / ( total / 100 )
I am not good in calculating, but in numbers it's working.
all the best

How can I create a list of weeks in access?

How do I create a query which breaks down a frequency of counts based on a list of weeks between 2 different dates in Access?
At the moment I have the following code in t-sql, but would like to have it run in Access.
declare #fromdate smalldatetime
declare #todate smalldatetime
declare #toptr smalldatetime
declare #fromptr smalldatetime
set #fromdate = '1/11/2010'
set #todate = '27/12/2010'
set #fromptr = dateadd(dd,1 - datepart(weekday,#fromdate), #fromdate)
while #fromptr < #todate
begin
print 'from: ' + cast(#fromptr as nvarchar) + ' --> ' + cast(#toptr as nvarchar)
set #fromptr = dateadd(dd,7, #fromptr)
set #toptr = dateadd(dd,7, #fromptr)
insert into #weeks values (#fromptr, #toptr)
end
I want to somehow bind some rows with lots of dates in them and aggregate them per 'week- ending date' from the dates creating in the table variable. Access doesn't seem to allow this kind of sql query, so was wondering if there was another way of doing this:
1) either by not using an intermediate table at all, 2) and/or converting the above code into access compatible
This will group by week (starting with Sunday) and be faster than other date calculation methods like DateAdd, DateDiff, DatePart, and Format.
SELECT
CDate((([DateColumn] - 1) \ 7) * 7 + 1) AS WeekStartingDate,
Sum([OrderCount]) AS SumOfOrders
FROM
Orders
GROUP BY
CDate((([DateColumn] - 1) \ 7) * 7 + 1);
If you want to see week ending date, add 7 at the end instead of 1. The GROUP BY expression can probably be just ([DateColumn] - 1) \ 7 but I'm not sure.
The backslash performs integer division, dividing by 7 converts a week of dates to a single integer, and the -1 adjusts for the fact that the "zero date" is a Saturday rather than a Sunday. To use a different starting day of the week, adjust the -1 and +1 by the same amount. To use Monday, for example, it would be -2 and +2.
This is language and region independent by depending on VB's internal representation of dates as numbers.
You can use Format in Access queries: http://msdn.microsoft.com/en-us/library/aa159657(v=office.10).aspx
SELECT Format(Date,"ww") FROM Table
GROUP BY Format(Date,"ww")
The plain-vanilla solution is to introduce a Calendar table which may look something like
Calendar
------------------------
FullDate date
CalendarYear integer
DayNumberInWeek integer
DayNumberInMonth integer
DayNumberInYear integer
DayNumberInEpoch integer
WeekNumberInYear integer
WeekNumberInEpoch integer
MonthNumberInYear integer
MonthNumberInEpoch integer
... and many more that you may need to group by
Then if you have table Counters
Counters
-----------
FullDate date
Value integer -- cumulative for one day
You can:
select
WeekNumberInYear
, sum(Value)
from Calendar as a
join Counters as b on b.FullDate = a.FullDate
where CalendarYear = 2010
group by WeekNumberInYear ;
The easiest way to populate the Calendar is to spend some time in Excel, create 10-20 years worth of rows and simply import in a DB.
Nothing specific to Access here, but hope you get the idea.

Find rows in table that fall under minimum and maximum range

Following is the table and script of this table.
DECLARE #temp TABLE (PPId INT, SVPId INT, Minimum INT, Maximum INT)
INSERT INTO #temp VALUES(1,1,8,20)
INSERT INTO #temp VALUES(2,1,21,100)
Minimum & Maximum are passed in as parameter. I want to find all rows that fall in the given range.
E.g.;
If #minimum = 9 and #maximum = 15
then it falls in the range of first
row.
If #minimum = 21 and #maximum = 22
then it falls in the range of 2nd
row.
If #minimum = 7 and #maximum = 25
then it falls in the range of both
rows so both rows should be returned.
Thanks.
When comparing ranges like this, it's easier to look for the case where ranges don't overlap. There are many different ways that two ranges can overlap, but there is only two ways that the don't overlap:
select *
from #temp
where not (#maximum < Minimum or #minimum > Maximum)
SELECT *
FROM #temp
WHERE minimum <= #max
AND maximum >= #min
My suggested answer is so simple I suspect either I'm missing something or the question is not complete?
SELECT *
FROM #temp
WHERE Minimum < #Minimum
AND Maximum > #Maximum
I can see what you're trying to do. You want to know how many min/max ranges overlap with the provide min/max range. Try this:
SELECT * FROM #temp T
WHERE #minimum BETWEEN T.minimum AND T.maximum
OR #maximum BETWEEN T.minimum AND T.maximum
OR T.minimum BETWEEN #minimum AND #maximum
OR T.maximum BETWEEN #minimum AND #maximum
This should return all rows that intersect with the interval [#minimum, #maximum].