Running (Average True Range) calculation in a column SQL - sql-server

I have a dataset of price data and would like to get the calculation of the ongoing ATR (Average True Range) for all rows > 21. Row 21 is the AVG([TR]) from Rows 2-21 and is equal to 353.7.
The calculation that needs to be continuous for the rest of that [ATR_20] column will need to be:
ATR_20 (after row 21) = (([Previous ATR_20]*19)+[TR])/20
My dataset:
Date Open High Low Close TotalVolume Prev_Close TR_A TR_B TR_C TR ATR
2017-02-01 5961 5961 5425 5498 22689 NULL 536 NULL NULL NULL NULL
2017-02-02 5697 5868 5615 5734 22210 5498 253 370 117 370 NULL
2017-02-03 5742 5811 5560 5725 15852 5734 251 77 174 251 NULL
2017-02-06 5675 5679 5545 5554 9777 5725 134 46 180 180 NULL
2017-02-07 5597 5613 5426 5481 12692 5554 187 59 128 187 NULL
2017-02-08 5459 5630 5450 5625 9134 5481 180 149 31 180 NULL
2017-02-09 5615 5738 5532 5668 10630 5625 206 113 93 206 NULL
2017-02-10 5651 5661 5488 5602 9709 5668 173 7 180 180 NULL
2017-02-13 5700 6195 5639 6161 26031 5602 556 593 37 593 NULL
2017-02-14 6197 6594 6073 6571 35969 6161 521 433 88 521 NULL
2017-02-15 6510 6650 6275 6492 22046 6571 375 79 296 375 NULL
2017-02-16 6505 6680 6325 6419 12515 6492 355 188 167 355 NULL
2017-02-17 6434 6670 6429 6658 14947 6419 241 251 10 251 NULL
2017-02-21 6800 6957 6603 6654 23838 6658 354 299 55 354 NULL
2017-02-22 6704 6738 6145 6222 25004 6654 593 84 509 593 NULL
2017-02-23 6398 6437 5901 6343 46677 6222 536 215 321 536 NULL
2017-02-24 5280 5589 5260 5404 51757 6343 329 754 1083 1083 NULL
2017-02-27 5437 5461 5260 5300 19831 5404 201 57 144 201 NULL
2017-02-28 5258 5410 5167 5195 15900 5300 243 110 133 243 NULL
2017-03-01 5251 5299 5052 5215 16958 5195 247 104 143 247 NULL
2017-03-02 5160 5231 5063 5130 17805 5215 168 16 152 168 353.7
2017-03-03 5141 5363 5088 5320 14516 5130 275 233 42 275 NULL
I got to this point by the following
WITH cte_ACIA ([RowNumber], [Date], [Open], [High], [Low], [Close],
[Prev_Close], [TotalVolume], [TR_A], [TR_B], [TR_C])
AS
(SELECT
ROW_NUMBER() OVER (ORDER BY [Date] ASC) RowNumber,
[Date],
[Open],
[High],
[Low],
[Close],
LAG([Close]) OVER(ORDER BY [Date]) AS Prev_Close,
[TotalVolume],
ROUND([High]-[Low], 5) AS TR_A,
ABS(ROUND([High]-LAG([Close]) OVER(ORDER BY [Date]), 5)) AS TR_B,
ABS(ROUND([Low]-LAG([Close]) OVER(ORDER BY [Date]), 5)) AS TR_C,
FROM NASDAQ.ACIA_TEMP)
SELECT [RowNumber], [Date], [Open], [High], [Low], [Close], [Prev_Close],
[TotalVolume], [TR_A], [TR_B], [TR_C], [TR],
CASE
WHEN RowNumber = 21 THEN AVG([TR]) OVER (ORDER BY [Date] ASC ROWS 19 PRECEDING)
END AS ATR_20
FROM
(
SELECT [RowNumber],[Date],[Open],[High],[Low],[Close],
IIF(RowNumber = 1, NULL, Prev_Close) Prev_Close,
[TotalVolume],
[TR_A],
IIF(RowNumber > 1, [TR_B], NULL) TR_B,
IIF(RowNumber > 1, [TR_C], NULL) TR_C,
CASE
WHEN TR_A > TR_B AND TR_A > TR_C THEN TR_A
WHEN TR_B > TR_A AND TR_B > TR_C THEN TR_B
ELSE TR_C
END AS TR
FROM cte_ACIA) sub
Please let me know if you have questions or I need to clarify anything.

I suppose you are just looking for a hint. Otherwise you would have posted your table definition. We can't construct a query for you since we don't have the basic pieces. However, here's the hint! Use an aggregating window function with the OVER clause specifying ROWS PRECEDING.
See SELECT - OVER Clause

Related

Time attendance SQL statement from to

we use MS SQL 2008 r2 and
we have this 2 table its for time attendance
select CHECKINOUT.USERID ,USERINFO.USERID,CHECKINOUT.SENSORID,
userinfo.BADGENUMBER,CHECKINOUT.CHECKTIME
from CHECKINOUT , userinfo
where CHECKINOUT.USERID=userinfo.userid
and userinfo.BADGENUMBER=63 and CHECKTIME
between '2017-03-26' and '2017-04-26'
USERID USERID SENSORID BADGENUMBER CHECKTIME
181 181 100 63 2017-04-01 11:02:19.000
181 181 100 63 2017-04-02 10:12:52.000
181 181 100 63 2017-04-03 10:32:43.000
181 181 1 63 2017-04-04 19:21:26.000
181 181 100 63 2017-04-05 19:54:30.000
181 181 100 63 2017-04-06 10:00:52.000
181 181 100 63 2017-04-07 09:54:49.000
181 181 100 63 2017-04-08 10:40:20.000
181 181 100 63 2017-04-11 10:21:47.000
181 181 100 63 2017-04-12 13:32:00.000
181 181 100 63 2017-04-12 19:17:13.000
181 181 100 63 2017-04-13 11:07:02.000
181 181 100 63 2017-04-13 18:34:37.000
181 181 100 63 2017-04-15 18:49:02.000
181 181 100 63 2017-04-15 18:49:10.000
181 181 1 63 2017-04-16 10:17:58.000
181 181 100 63 2017-04-18 10:31:47.000
181 181 100 63 2017-04-18 18:39:54.000
181 181 100 63 2017-04-19 10:54:00.000
181 181 100 63 2017-04-20 10:45:03.000
181 181 100 63 2017-04-22 10:25:09.000
181 181 100 63 2017-04-22 18:45:21.000
181 181 100 63 2017-04-22 18:45:26.000
181 181 100 63 2017-04-23 10:18:01.000
181 181 100 63 2017-04-24 10:20:59.000
181 181 100 63 2017-04-24 18:41:07.000
181 181 100 63 2017-04-25 11:13:48.000
and we need to have from this 2 table check date and chick in and check out and sensor id
(Main(CHECKINOUT.CHECKTIME) as Checkin , Max(CHECKINOUT.CHECKTIME)as Check out )
first check in and last check out and the time between Min and Max its from 6:00 AM and 5:00 AM next day its 23 hours and the subtract between checkin and checkout as hours
for example
USERID USERID SENSORID BADGENUMBER CHECKTIME
181 181 100 63 2017-04-01 10:02:19.000
181 181 100 63 2017-04-01 18:12:52.000
181 181 100 63 2017-04-02 10:32:43.000
181 181 100 63 2017-04-03 01:21:26.000
181 181 100 63 2017-04-04 10:54:30.000
181 181 100 63 2017-04-04 18:00:52.000
181 181 100 63 2017-04-05 09:54:49.000
181 181 100 63 2017-04-05 23:40:20.000
181 181 100 63 2017-04-06 10:21:47.000
181 181 100 63 2017-04-07 03:32:00.000
to be like that
BADGENUMBER SENSORID CHECKDate Checkin Checkout Hours
63 100 2017-04-01 10:02:19 18:12:52 8:10
63 100 2017-04-02 10:32:43 01:21:26 14:49
63 100 2017-04-04 10:54:30 18:00:52 07:06
63 100 2017-04-05 09:54:49 23:40:20 13:46
63 100 2017-04-06 10:21:47 03:32:00 17:11
This might get you the required result
select u.BADGENUMBER, attendance.SENSORID, attendance.CHECKDate, attendance.CheckIn ,attendance.CheckOut
,cast(DATEDIFF(n, attendance.CheckIn, attendance.checkout) / 60 as varchar) + ':' + cast(DATEDIFF(n, attendance.CheckIn, attendance.checkout) % 60 as varchar) as Minutes
from (
select temp.USERID, temp.SENSORID, temp.CHECKDate ,temp.CheckIn
,case when temp.COut = temp.CheckIn then null when temp.CheckIn is null then null else temp.COut end as CheckOut
from(
select c.USERID, c.SENSORID, convert(date, CHECKTIME) CHECKDate
,(select min(CHECKTIME) from CHECKINOUT cinout where cinout.USERID = c.USERID and cinout.SENSORID = c.SENSORID and cinout.CheckTime >= dateadd(hour, 6, convert(datetime, convert(date, c.CheckTime)))) CheckIn
,(select max(CHECKTIME) from CHECKINOUT cinout where cinout.USERID = c.USERID and cinout.SENSORID = c.SENSORID and cinout.CheckTime <= dateadd(hour, 29, convert(datetime, convert(date, c.CheckTime)))) COut
from CHECKINOUT c
group by c.USERID, c.SENSORID, convert(date, CHECKTIME)
)temp
) attendance
inner join userinfo u on u.USERID = attendance.USERID
where u.BADGENUMBER = 63 and CHECKDate
between '2017-03-26' and '2017-04-26'

How can I get the differences between the OrderStop date and the OrderStart date in the next column?

How can I get the differences between the OrderStop and the OrderStart in the next column?
For example, row 1 has OrderStop date of 1/31/2007 I want to count the days between the next start date 1/26/2007 I know they are overlaps.
ID OrderStart OrderStop
132 4/14/2006 1/31/2007
132 1/26/2007 3/14/2007
132 2/1/2007 3/2/2007
132 3/2/2007 3/14/2007
132 3/14/2007 1/8/2010
132 11/26/2008 1/20/2011
132 1/8/2010 7/14/2010
132 7/14/2010 8/15/2012
132 8/15/2012 1/17/2013
132 1/17/2013 3/22/2013
132 3/21/2013 5/2/2013
132 5/2/2013 8/2/2013
132 5/22/2013 8/2/2013
132 7/29/2013 3/6/2014
132 3/5/2014 7/16/2014
132 7/16/2014 6/19/2015
132 8/21/2014 6/19/2015
132 6/19/2015 4/1/2016
132 6/25/2015 9/9/2015
132 4/1/2016 5/3/2016
132 5/3/2016 7/27/2016
132 8/15/2016 11/2/2016
I am trying to accomplished the below. How can I create a SQL statement that can accomplish this?
132 4/14/2006 1/31/2007
132 1/26/2007 4/1/2016
132 4/1/2016 7/27/2016
132 8/15/2016 11/2/2016
Consumable sample data makes things much easier for us. So does the version of SQL server. Below is a solution that uses 2012's LEAD functionality. The second one is for pre-2012 systems; it accomplishes the same thing but requires a self join and will not be as efficient.
declare #orders table (id int, orderStart date, orderStop date);
insert #orders
values
(132,'4/14/2006 ','1/31/2007'),
(132,'1/26/2007 ','3/14/2007'),
(132,'2/1/2007 ','3/2/2007 '),
(132,'3/2/2007 ','3/14/2007'),
(132,'3/14/2007 ','1/8/2010 '),
(132,'11/26/2008','1/20/2011'),
(132,'1/8/2010 ','7/14/2010'),
(132,'7/14/2010 ','8/15/2012'),
(132,'8/15/2012 ','1/17/2013'),
(132,'1/17/2013 ','3/22/2013'),
(132,'3/21/2013 ','5/2/2013 '),
(132,'5/2/2013 ','8/2/2013 '),
(132,'5/22/2013 ','8/2/2013 '),
(132,'7/29/2013 ','3/6/2014 '),
(132,'3/5/2014 ','7/16/2014'),
(132,'7/16/2014 ','6/19/2015'),
(132,'8/21/2014 ','6/19/2015'),
(132,'6/19/2015 ','4/1/2016 '),
(132,'6/25/2015 ','9/9/2015 '),
(132,'4/1/2016 ','5/3/2016 '),
(132,'5/3/2016 ','7/27/2016'),
(132,'8/15/2016 ','11/2/2016');
select *,
nextStart = lead(orderStart,1) over (order by orderStart),
daysBetween = abs(datediff(day,lead(orderStart,1) over (order by orderStart), orderStop))
from #orders
order by orderStart;
with preSort as
(
select *, rn = row_number() over (order by orderstart)
from #orders
)
select p2.id, p2.orderStart, p2.orderStop , nextStart = p1.orderStart,
daysBetween = abs(datediff(day, p2.orderStop, p1.orderStart))
from preSort p1 join preSort p2 on p1.rn = p2.rn+1
order by p1.orderStart;
Both Return
id orderStart orderStop nextStart daysBetween
----------- ---------- ---------- ---------- -----------
132 2006-04-14 2007-01-31 2007-01-26 5
132 2007-01-26 2007-03-14 2007-02-01 41
132 2007-02-01 2007-03-02 2007-03-02 0
132 2007-03-02 2007-03-14 2007-03-14 0
132 2007-03-14 2010-01-08 2008-11-26 408
132 2008-11-26 2011-01-20 2010-01-08 377
132 2010-01-08 2010-07-14 2010-07-14 0
132 2010-07-14 2012-08-15 2012-08-15 0
132 2012-08-15 2013-01-17 2013-01-17 0
132 2013-01-17 2013-03-22 2013-03-21 1
132 2013-03-21 2013-05-02 2013-05-02 0
132 2013-05-02 2013-08-02 2013-05-22 72
132 2013-05-22 2013-08-02 2013-07-29 4
132 2013-07-29 2014-03-06 2014-03-05 1
132 2014-03-05 2014-07-16 2014-07-16 0
132 2014-07-16 2015-06-19 2014-08-21 302
132 2014-08-21 2015-06-19 2015-06-19 0
132 2015-06-19 2016-04-01 2015-06-25 281
132 2015-06-25 2015-09-09 2016-04-01 205
132 2016-04-01 2016-05-03 2016-05-03 0
132 2016-05-03 2016-07-27 2016-08-15 19
132 2016-08-15 2016-11-02 NULL NULL

How to get the latest value per time interval in SQL Server

SELECT RIGHT(timestamp,LEN(timestamp) -12) as DailyTime, left(roundtrip, LEN(roundtrip) -2) as HalfHourDuration, site_code
FROM tblServer_Status
WHERE timestamp >= dateadd(day, datediff(day,'19000101',CURRENT_TIMESTAMP),'19000101') AND timestamp < dateadd(day, datediff(day,'19000101',CURRENT_TIMESTAMP)+1,'19000101') AND server = 'ServerName' AND site_code = 'A'
GROUP BY timestamp, roundtrip, site_code HAVING(((COUNT(site_code))>0))
ORDER BY timestamp
I have this code that gives me this kind of output
| DailyTime | HalfHourDuration | Site_Code|
12:00AM 122 A
12:00AM 143 A
12:00AM 242 A
12:30AM 112 A
12:30AM 222 A
12:30AM 462 A
01:00AM 322 A
01:00AM 642 A
01:00AM 322 A
01:30AM 146 A
01:30AM 167 A
01:30AM 116 A
02:00AM 163 A
02:00AM 145 A
02:00AM 121 A
02:30AM 149 A
02:30AM 135 A
02:30AM 111 A
...................................
But I need to get the Latest duration per time.
Like this one
| DailyTime | HalfHourDuration | Site_Code|
12:00AM 242 A
12:30AM 462 A
01:00AM 322 A
01:30AM 116 A
02:00AM 121 A
02:30AM 111 A
Something like that.
can anyone help me configure my codes.
Thanks.
You can do this using row_number():
with t as (
<your query here without order by>
)
select t.*
from (select t.*,
row_number() over (partition by DailyTime
order by HalfHourDuration desc
) as seqnum
from t
) t
where seqnum = 1;
Add Max function to your column HalfHourDuration.
This will list out the maximum value of the roundtrip alone grouping by timestamp

Average value depending on cumulative value from other column aka running total

I've sifted through the various sql-server tagged threads using AVERAGE and Cumulative as search terms. Various desperate answers, but I can't cobble them together for my needs. The use case is to find the initial average value (cumulative value/cumulative days on) for a time period when cumulative days on is greater than 60 and less than 90.
Below is a table where ID identifies the object, VALUE is the amount reported on a monthly basis and DAYSON is the number of days in that month where the object ran to produce the value. YEARMONTH is date value on which on can sort.
ID VALUE DASYON YEARMONTH
1 166 27 201502
1 1 2 201505
1 569 19 201507
1 312 19 201508
2 364 27 201502
2 328 31 201503
2 242 29 201504
2 273 31 201505
2 174 30 201506
2 188 25 201507
2 203 25 201508
3 474 28 201502
3 521 31 201503
3 465 30 201504
3 473 31 201505
3 434 30 201506
3 404 31 201507
I would like to create a summary table that averages the cumulative value divided by the cumulative days uniquely for each ID where cumulative days is greater than 60 and less than 90. Below is a table that with the cumulative values. (I generated this in Excel)
ID VALUE cumValue DASYON cumDaysOn YEARMONTH
1 166 166 27 27 201502
1 1 167 2 29 201505
1 569 736 19 48 201507
1 312 1048 19 67 201508
2 364 364 27 27 201502
2 328 692 31 58 201503
2 242 934 29 87 201504
2 273 1207 31 118 201505
2 174 1381 30 148 201506
2 188 1569 25 173 201507
2 203 1772 25 198 201508
3 474 474 28 28 201502
3 521 505 31 59 201503
3 465 535 30 89 201504
3 473 566 31 120 201505
3 434 596 30 150 201506
3 404 627 31 181 201507
I try this based on other threads:
SELECT
ID,
Value,
SUM(Value) OVER (ORDER BY ID, YearMonth) [cumValue],
DaysOn,
SUM (DaysOn) OVER (Order by ID, YearMonth) as cumDaysOn,
YearMonth
FROM table
WHERE DAYSON > 0 and Liquid > 0 and YearMonth > 201501
GROUP BY ID, YearMonth, Value, DaysOn
ORDER BY ID, yearmonth
I can't get it to iterate over the ID; it just keeps summing down the column. If I could create a table or view like the one above, then I could always use a select statement and divide cumvalue by cumdayson.
Below is a table to show where I would get the initial average value (InititalAverageValue) based on the criteria:
ID VALUE cumValue DASYON cumDaysOn YEARMONTH InitalAvgValue
1 166 166 27 27 201502
1 1 167 2 29 201505
1 569 736 19 48 201507
1 312 1048 19 67 201508 55
2 364 364 27 27 201502
2 328 692 31 58 201503
2 242 934 29 87 201504 32
2 273 1207 31 118 201505
2 174 1381 30 148 201506
2 188 1569 25 173 201507
2 203 1772 25 198 201508
3 474 474 28 28 201502
3 521 505 31 59 201503
3 465 535 30 89 201504 18
3 473 566 31 120 201505
3 434 596 30 150 201506
3 404 627 31 181 201507
Ultimately what I desire is table as such:
ID InitalAvgValue
1 55
2 32
3 18
Thanks in advance for any help.
The crux is that you need a running total. There are several approaches to calculating running totals, but they have various tradeoffs between simplicity and performance. The "best" approach depends on the expected size of your data set and whether you are using SQL Server 2012 or an earlier version. The following article describes some different options along with the pros and cons:
http://sqlperformance.com/2012/07/t-sql-queries/running-totals
Here's a quick example using correlated subqueries, which may be reasonable for small data sets, but likely would not scale well to larger data:
SELECT
ID,
ROUND(AVG(CAST(CumulativeValue AS FLOAT) / CAST(CumulativeDaysOn AS FLOAT)), 1) AS Average
FROM
(
SELECT
ID,
Value,
DaysOn,
(SELECT SUM(Value) FROM ExampleTable t2 WHERE t1.ID = t2.ID and t2.YearMonth <= t1.YearMonth) AS CumulativeValue,
(SELECT SUM(DaysOn) FROM ExampleTable t2 WHERE t1.ID = t2.ID and t2.YearMonth <= t1.YearMonth) AS CumulativeDaysOn
FROM
ExampleTable t1
) AS ExampleWithTotals
WHERE
CumulativeDaysOn > 60 AND CumulativeDaysOn < 90
GROUP BY
ID
ORDER BY
ID
;
Output:
ID Average
1 15.6
2 10.7
3 16.4

how to get result from this data

I want to compute result from this table.
I want quantity 1 - quantity2 as another column in the table shown below.
this table has more such records
I am trying to query but not been able to get result.
select * from v order by is_active desc, transaction_id desc
PK_GUEST_ITEM_ID FK_GUEST_ID QUANTITY TRANSACTION_ID IS_ACTIVE
---------------- -------------------- ---------------------- -------------------- -----------
12963 559 82000 795 1
12988 559 79000 794 0
12987 559 76000 793 0
12986 559 73000 792 0
12985 559 70000 791 0
12984 559 67000 790 0
12983 559 64000 789 0
12982 559 61000 788 0
12981 559 58000 787 0
12980 559 55000 786 0
12979 559 52000 785 0
12978 559 49000 784 0
12977 559 46000 783 0
12976 559 43000 782 0
I want another column that will contain the subtraction of two quantities .
DESIRED RESULT SHOULD BE SOMETHING LIKE THIS
PK_GUEST_ITEM_ID FK_GUEST_ID QUANTITY Result TRANSACTION_ID IS_ACTIVE
---------------- -------------------- ---------------------- -------------------- -----------
12963 559 82000 3000 795 1
12988 559 79000 3000 794 0
12987 559 76000 3000 793 0
12986 559 73000 3000 792 0
12985 559 70000 3000 791 0
12984 559 67000 3000 790 0
12983 559 64000 3000 789 0
12982 559 61000 3000 788 0
12981 559 58000 3000 787 0
12980 559 55000 3000 786 0
12979 559 52000 3000 785 0
12978 559 49000 3000 784 0
12977 559 46000 3000 783 0
12976 559 43000 NULL 782 0
to get the next lower transaction id you can use a subquery
Select max(transactionid)
from vinner
where vinner.tr.ansactionid <vouter.transactionid
This works fine for me:
select v1.transactionid as HigherTransactionID
,v2.transactionid as LowerTransactionId
,v1.quantity as HigherQuan
,v2.quantity as LowerQuan
,v1.quantity - v2.quantity as Result
from v as v1
left join v as v2 on
v2.transactionid =
(Select MAX(v.transactionid)
from v
where v.transactionid < v1.transactionid)
Tested with following Table:
quantity transactionid
8200 795
7900 794
6600 793
6300 792
6000 788
5700 787
4300 786
With following result:
HigherTransactionID LowerTransactionId HigherQuan LowerQuan Result
795 794 8200 7900 300
794 793 7900 6600 1300
793 792 6600 6300 300
792 788 6300 6000 300
788 787 6000 5700 300
787 786 5700 4300 1400
786 NULL 4300 NULL NULL
Hope this is what you expected
Try something like this:
SELECT
v1.*,
v1.QUANTITY - v2.QUANTITY AS result
FROM
v AS v1
-- always join transaction (some will be NULL in result)
LEFT JOIN
v AS v2
ON
-- join to lower trans id
v2.PK_TRANSACTION_ID = v1.PK_TRANSACTION_ID-1
WHERE
-- get only odd trans ids
v1.PK_TRANSACTION_ID % 2 = 1
ORDER BY
is_active DESC,
transaction_id DESC
But i am afraid it won't be deamon of speed :(.
select order2.Quantity-order1.Quantity,order1.fk_guestId, ...from v order1
join v order2 on order1.Transaction_id=order2.transaction_id+1
by is_active desc, transaction_id desc

Resources