Find rows in table that fall under minimum and maximum range - sql

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].

Related

Decimal to time SQL Server

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

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

Find closest date in SQL Server

I have a table dbo.X with DateTime column Y which may have hundreds of records.
My Stored Procedure has parameter #CurrentDate, I want to find out the date in the column Y in above table dbo.X which is less than and closest to #CurrentDate.
How to find it?
The where clause will match all rows with date less than #CurrentDate and, since they are ordered descendantly, the TOP 1 will be the closest date to the current date.
SELECT TOP 1 *
FROM x
WHERE x.date < #CurrentDate
ORDER BY x.date DESC
Use DateDiff and order your result by how many days or seconds are between that date and what the Input was
Something like this
select top 1 rowId, dateCol, datediff(second, #CurrentDate, dateCol) as SecondsBetweenDates
from myTable
where dateCol < #currentDate
order by datediff(second, #CurrentDate, dateCol)
I have a better solution for this problem i think.
I will show a few images to support and explain the final solution.
Background
In my solution I have a table of FX Rates. These represent market rates for different currencies. However, our service provider has had a problem with the rate feed and as such some rates have zero values. I want to fill the missing data with rates for that same currency that as closest in time to the missing rate. Basically I want to get the RateId for the nearest non zero rate which I will then substitute. (This is not shown here in my example.)
1) So to start off lets identify the missing rates information:
Query showing my missing rates i.e. have a rate value of zero
2) Next lets identify rates that are not missing.
Query showing rates that are not missing
3) This query is where the magic happens. I have made an assumption here which can be removed but was added to improve the efficiency/performance of the query. The assumption on line 26 is that I expect to find a substitute transaction on the same day as that of the missing / zero transaction.
The magic happens is line 23: The Row_Number function adds an auto number starting at 1 for the shortest time difference between the missing and non missing transaction. The next closest transaction has a rownum of 2 etc.
Please note that in line 25 I must join the currencies so that I do not mismatch the currency types. That is I don't want to substitute a AUD currency with CHF values. I want the closest matching currencies.
Combining the two data sets with a row_number to identify nearest transaction
4) Finally, lets get data where the RowNum is 1
The final query
The query full query is as follows;
; with cte_zero_rates as
(
Select *
from fxrates
where (spot_exp = 0 or spot_exp = 0)
),
cte_non_zero_rates as
(
Select *
from fxrates
where (spot_exp > 0 and spot_exp > 0)
)
,cte_Nearest_Transaction as
(
select z.FXRatesID as Zero_FXRatesID
,z.importDate as Zero_importDate
,z.currency as Zero_Currency
,nz.currency as NonZero_Currency
,nz.FXRatesID as NonZero_FXRatesID
,nz.spot_imp
,nz.importDate as NonZero_importDate
,DATEDIFF(ss, z.importDate, nz.importDate) as TimeDifferece
,ROW_NUMBER() Over(partition by z.FXRatesID order by abs(DATEDIFF(ss, z.importDate, nz.importDate)) asc) as RowNum
from cte_zero_rates z
left join cte_non_zero_rates nz on nz.currency = z.currency
and cast(nz.importDate as date) = cast(z.importDate as date)
--order by z.currency desc, z.importDate desc
)
select n.Zero_FXRatesID
,n.Zero_Currency
,n.Zero_importDate
,n.NonZero_importDate
,DATEDIFF(s, n.NonZero_importDate,n.Zero_importDate) as Delay_In_Seconds
,n.NonZero_Currency
,n.NonZero_FXRatesID
from cte_Nearest_Transaction n
where n.RowNum = 1
and n.NonZero_FXRatesID is not null
order by n.Zero_Currency, n.NonZero_importDate

calculating "Max Draw Down" in SQL

edit: it's worth reviewing the comments section of the first answer to get a clearer idea of the problem.
edit: I'm using SQLServer 2005
something similar to this was posted before but I don't think enough information was given by the poster to truly explain what max draw down is. All my definitions of max draw down come from (the first two pages of) this paper:
http://www.stat.columbia.edu/~vecer/maxdrawdown3.pdf
effectively, you have a few terms defined mathematically:
Running Maximum, Mt
Mt = maxu in [0,t] (Su)
where St is the price of a Stock, S, at time, t.
Drawdown, Dt
Dt = Mt - St
Max Draw Down, MDDt
MDDt = maxu in [0,t] (Du)
so, effectively what needs to be determined is the local maximums and minimums from a set of hi and low prices for a given stock over a period of time.
I have a historical quote table with the following (relevant) columns:
stockid int
day date
hi int --this is in pennies
low int --also in pennies
so for a given date range, you'll see the same stockid every day for that date range.
EDIT:
hi and low are high for the day and low for each day.
once the local max's and min's are determined, you can pair every max with every min that comes after it and calculate the difference. From that set, the maximum difference would be the "Max Draw Down".
The hard part though, is finding those max's and min's.
edit: it should be noted:
max drawdown is defined as the value of the hypothetical loss if the stock is bought at it's highest buy point and sold at it's lows sell point. A stock can't be sold at a minval that came before a maxval. so, if the global minval comes before the global maxval, those two values do not provide enough information to determine the max-drawdown.
Brutally inefficient, but very simple version using a view is below:
WITH DDView
AS (SELECT pd_curr.StockID,
pd_curr.Date,
pd_curr.Low_Price AS CurrPrice,
pd_prev.High_Price AS PrevPrice,
pd_curr.Low_Price / pd_prev.High_Price - 1.0 AS DD
FROM PriceData pd_curr
INNER JOIN PriceData pd_prev
ON pd_curr.StockID = pd_prev.StockID
AND pd_curr.Date >= pd_prev.Date
AND pd_curr.Low_Price <= pd_prev.High_Price
AND pd_prev.Date >= '2001-12-31' -- #param: min_date of analyzed period
WHERE pd_curr.Date <= '2010-09-31' -- #param: max_date of analyzed period
)
SELECT dd.StockID,
MIN(COALESCE(dd.DD, 0)) AS MaxDrawDown
FROM DDView dd
GROUP BY dd.StockID
As usually you would perform the analysis on specific time period, it would make sense to wrap the query in a stored procedure with the parameters #StartDate, #EndDate and possibly #StockID. Again, this is quite inefficient by design - O(N^2), but if you have good indices and not huge amount of data, SQL Server will handle it pretty good.
Some things we need to consider in the problem domain:
Stocks have a range of prices every day, often viewed in candlestick charts
lets call the highest price of a day HI
lets call the lowest price of a day LOW
the problem is constrained by time, even if the time constraints are the IPO date and Delisting Dates
the maximum drawdown is the most you could possibly lose on a stock over that timeframe
assuming a LONG strategy: logically if we are able to determine all local maxes (MAXES) and all local mins (MINS) we could define a set of where we pair each MAX with each subsequent MIN and calculate the difference DIFFS
Sometimes the difference will result in a negative number, however that is not a drawdown
therefore, we need to select append 0 in the set of diffs and select the max
The problem lies in defining the MAXES and the MINS, with the function of the curve we could apply calculus, bummer we can't. Obviously
the maxes need to come from the HI and
the MINS need to come from the LOW
One way to solve this is to define a cursor and brute force it. Functional languages have nice toolsets for solving this as well.
For SQL Server and for one stock at a time, try this:
Create Procedure 'MDDCalc'(
#StartDate date,
#EndDate date,
#Stock int)
AS
DECLARE #MinVal Int
DECLARE #MaxVal Int
DECLARE #MaxDate date
SET #MaxVal = (
SELECT MAX(hi)
FROM Table
WHERE Stockid = #Stock
AND Day BETWEEN (#Startdate-1) AND (#EndDate+1))
SET #MaxDate=(
SELECT Min(Date)
FROM Table
WHERE Stockid = #Stock
AND hi = #MaxVal)
SET #MinVal = (
SELECT MIN(low)
FROM Table
WHERE Stockid = #Stock
AND Day BETWEEN (#MaxDate-1) AND (#EndDate+1))
SELECT (#MaxVal-#MinVal) AS 'MDD'
I have encounter this problem recently, My solution is like this:
let data: 3,5,7,3,-1,3,-8,-3,0,10
add the sum one by one, if the sum is great than 0, set it 0, else get the sum, the result would be like this
0,0,0,0,-1,0,-8,-11,-11,-1
The Maximum draw down is the lowest value in the data, -11.
Is this what you're after?
select StockID,max(drawdown) maxdrawdown
from (
select h.StockID,h.day highdate,l.day lowdate,h.hi - l.lo drawdown
from mdd h
inner join mdd l on h.StockID = l.StockID
and h.day<l.day) x
group by StockID;
It's a SQL based brute force approach. It compares every low price after today's hi price within the same stock and finds the greatest difference between the two prices. This will be the Maximum Draw Down.
It doesn't compare consider the same day as possible for maximum draw down as we don't have enough info in the table to determine if the Hi price happened before the Lo price on the day.
Here is a SQL Server 2005 user-defined function that should return the correct answer for a single stockid very efficiently
CREATE FUNCTION dbo.StockMaxDD(#StockID int, #day datetime) RETURNS int AS
BEGIN
Declare #MaxVal int; Set #MaxVal = 0;
Declare #MaxDD int; Set #MaxDD = 0;
SELECT TOP(99999)
#MaxDD = CASE WHEN #MaxDD < (#MaxVal-low) THEN (#MaxVal-low) ELSE #MaxDD END,
#MaxVal = CASE WHEN hi > #MaxVal THEN hi ELSE #MaxVal END
FROM StockHiLo
WHERE stockid = #Stockid
AND [day] <= #day
ORDER BY [day] ASC
RETURN #MaxDD;
END
This would not, however, be very efficient for doing a number of stockids at the same time. If you need to do many/all of the stockids at once, then there is a similar, but substantially more difficult approach that can do that very efficiently.

Efficient way to calculate sum of deltas between consecutive rows?

I have a data set consisting of time-stamped values, and absolute (meter) values. Sometimes the meter values reset to zero, which means I have to iterate through and calculate a delta one-by-one, and then add it up to get the total for a given period.
For example:
Timestamp Value
2009-01-01 100
2009-01-02 105
2009-01-03 120
2009-01-04 0
2009-01-05 9
the total here is 29, calculated as:
(105 - 100) + (120 - 105) + (0) + (9 - 0) = 29
I'm using MS-SQL server for this, and open to any suggestions.
Right now, I'm using a cursor to do this, which checks that the delta isn't negative, and then totals it up:
DECLARE CURSOR curTest CURSOR FAST_FORWARD FOR
SELECT value FROM table ORDER BY timestamp
OPEN curTest
DECLARE #delta bigint, #current bigint, #last bigint
SET #delta = 0
FETCH curTest INTO #current
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#current IS NOT NULL) AND (#current > 0)
BEGIN
IF (#last IS NOT NULL) AND (#current > #last)
SET #delta = #delta + (#current - #last)
SET #last = #current
FETCH curTest INTO #current
END
END
CLOSE curTest
DEALLOCATE curTest
It would be nice to get a data set like:
Timestamp Value LastValue
2009-01-01 100 NULL
2009-01-02 105 100
2009-01-03 120 105
2009-01-04 0 120
2009-01-05 9 0
as then it would be easy to grab the deltas, filter for (Value > LastValue), and do a SUM().
I tried:
SELECT m1.timestamp, m1.value,
( SELECT TOP 1 m2.value FROM table WHERE m2.timestamp < m1.timestamp ORDER BY m2.timestamp DESC ) as LastValue
FROM table
but this actually turns out to be slower than the cursor: When I run these together in SQL studio with 'show execution plan' on, the relative cost of this is 100% (with 7 or 8 operations - the majority in a clustered index scan on timestamp), and the cursor is 0% (with 3 operations).
(What I'm not showing here for simplicity is that I have several different sets of numbers, with a foreign key in this table as well - so there is also always a WHERE clause limiting to a specific set. I have several places where I calculate these totals for a given time period for several sets at once, and thus it becomes quite the performance bottleneck. The non-cursor method can also be easily modified to GROUP BY the key and return all the sets at once - but this actually is even slower in my testing than running the cursor multiple times, because there is the additional overhead of the GROUP BY and SUM() operation, aside from it being slower overall anyways.)
Much the same...
create table #temp ([timestamp] date,value int);
insert into #temp (timestamp,value) values ('2009-01-01',100)
insert into #temp (timestamp,value) values ('2009-01-02',105)
insert into #temp (timestamp,value) values ('2009-01-03',120)
insert into #temp (timestamp,value) values ('2009-01-04',0)
insert into #temp (timestamp,value) values ('2009-01-05',9);
with numbered as
(
select ROW_NUMBER() over (order by timestamp) id,value from #temp
)
select sum(n1.value-n2.value) from numbered n1 join numbered n2 on n1.id=n2.id+1 where n1.value!=0
drop table #temp;
Result is 29, as specified.
Start with row_number, then join back to yourself.
with numbered as
(
SELECT value, row_number() over (order by timestamp) as Rownum
FROM table
)
select sum(n1.value - n2.value)
from numbered n1
join
numbered n2 on n1.Rownum = n2.Rownum +1
Actually... you only want to pick up increases... so put a WHERE clause in, saying "WHERE n1.value > n2.value".
And... make sure I've put them the right way around... I've just changed it from -1 to +1, because I think I had it flipped.
Easy!
Rob
There are too many unnecessary joins in your algorithm.
Calculating the difference between each meter reading and its subsequent meter reading is a waste of resources. As a real world example, imagine if my electric company read my meter each day to how much electricity I used, and summed daily values to determine my monthly total - it just doesn't make sense. They simply determine the total based on the start value and the end value!
Simply calculate the difference between the first and last readings and adjust to account for the 'resets'. Your formula simply becomes:
total value = (final value) - (initial value)
+ (miscellaneous reductions in value, i.e. resets)
total value = (9) - (100) + (120)
= 29
It's trivial to find the final value and initial value. Just find the total amount by which 'meter' was reduced during 'resets', and add this to the total. Unless there are more reset records than measurement records, this will always be more efficient.
To borrow from spender's solution, the 'reset' value could be calculated by
create table...
select sum(n1.value-n2.value) from numbered n1 join numbered n2
on n1.id=n2.id+1 where n1.value=0 //note value=0 rather than value!=0