Conditionally iterating through each value in a column - sql

I've put together a small database to keep track of some accumulator bets that a few of my colleagues and I place during the football season. Now I need to write a function to calculate the winnings for each selection. The key table is selection with the key fields for the function being odds and result_id where a result_id of 1 is a win. The stake is stored in the bet table. Below is the selection table which has two winners for bet_id 1, so the resulting equation would be ((#stake * 1.40) * 1.40). Is there a way using a cursor or a set based method to generate this value?
bet_id punter_id team_id odds result_id ground_id
1 1 24 1.40 1 1
1 1 48 1.60 2 1
1 1 89 1.60 2 1
1 2 8 1.40 1 1
1 2 11 1.60 2 1
1 2 107 1.60 2 1

Assuming you join on bet_id:
CREATE TABLE bet (bet_id int NOT NULL PRIMARY KEY,
stake float NOT NULL); -- or some floating point number type
To multiply a group we use the following relationship:
log( a * b * c ) = log(a) + log(b) + log(c)
And therefore for multiplication
a * b * c = exp( log( a * b * c ) ) = exp( sum( log(.) ) )
To calculate the factors
bet_id | odds_factor
1 | 1.40 * 1.40 = 1.96
we use the Common Table Expression CTE in this query
WITH factor( bet_id, odds_factor ) AS
( SELECT bet_id, exp( sum( log( odds ) ) )
FROM selection
WHERE result_id = 1 -- only winning odds in the grouping
GROUP BY bet_id )
SELECT b.bet_id,
b.stake,
f.odds_factor,
b.stake * f.odds_factor AS "total_odds"
FROM bet b
INNER JOIN factor f
ON b.bet_id = f.bet_id
-- ORDER BY b.bet_id -- optional ordering for readability
which should yield (untested)
bet_id | stake | odds_factor | total_odds
1 | 10.0 | 1.96 | 19.6

Think this is as simple as:
SELECT B.BET_ID,
S.PUNTER_ID,
((B.stake * S.odds) * S.odds)
FROM BET AS B
INNER JOIN SELECTION AS S ON B.Bet_id = S.Bet_id
WHERE S.result_id = 1

Related

What is the best way to duplicate rows based on multiple columns?

I have the following table below:
ID Start_Repeat_1 End_Repeat_1 Start_Repeat_2 End_Repeat_2
A 3 7 2 5
B 1 4 2 5
My goal is to duplicate "A" 5 times and "B" 4 times with the output below
ID Repeat_1 Repeat_2
A 3 2
A 4 3
A 5 4
A 6 5
A 7 NULL
B 1 2
B 2 3
B 3 4
B 4 5
The logic is that "A" needs to be duplicated with numbers between 3 and 7 in one column and numbers between 2 and 5 in another column so it needs to be duplicated at least 5 times.
The version below would also do. The order between the two columns does not matter.
ID Repeat_1 Repeat_2
A 3 2
A 7 5
A 6 3
A 5 NULL
A 4 4
Can someone help me with this using SQL Server 2018?
The dataset size is about 10,000 rows and each row is duplicated at most 10 times with a total of 10 columns like this
ID Repeat_1 Repeat_2 Repeat_3 Repeat_4 Repeat_10
A 3 2 1 1 1
B 7 5 1 1 1
You need to make use of a tally table. In the following solution, I have use a recursive cte to generate one.
Alternatively you can use a recursive cte to generate one on the fly
-- Tally Table
create table tally
(
n int
)
-- generate 1000 numbers for tally table
with cte as
(
select n = 0
union all
select n = n + 1
from cte
where n < 1000
)
insert into tally (n)
select n
from cte
The cross apply is to find the maximum difference between the repeat_1 or 2 etc.
-- The query
select t.ID,
Repeat_1 = case when n.n <= (End_Repeat_1 - Start_Repeat_1)
then t.Start_Repeat_1 + n.n
end,
Repeat_2 = case when n.n <= (End_Repeat_2 - Start_Repeat_2)
then t.Start_Repeat_2 + n.n
end,
Repeat_3 = case when n.n <= (End_Repeat_3 - Start_Repeat_3)
then t.Start_Repeat_3 + n.n
end
from tbl t
cross apply
(
select m = max(d)
from (
values
(End_Repeat_1 - Start_Repeat_1 + 1),
(End_Repeat_2 - Start_Repeat_2 + 1),
(End_Repeat_3 - Start_Repeat_3 + 1)
) n (d)
) m
inner join tally n on n.n >= 0
and n.n < m.m
dbfiddel demo
you can use recursive cte :
with cte as (
select * from test
union all
select Id
, case when Start_Repeat_1 + 1 > End_Repeat_1 then null else Start_Repeat_1 + 1 end
,End_Repeat_1
,case when Start_Repeat_2+ 1 > End_Repeat_2 then null else Start_Repeat_2+ 1 end
,End_Repeat_2
from cte
where Start_Repeat_1 <= End_Repeat_1 and Start_Repeat_2 <= End_Repeat_2
)
select ID,Start_Repeat_1,Start_Repeat_2
from cte
where coalesce(Start_Repeat_1,Start_Repeat_2) is not null
order by ID
ID | Start_Repeat_1 | Start_Repeat_2
:- | -------------: | -------------:
A | 3 | 2
A | 4 | 3
A | 5 | 4
A | 6 | 5
A | 7 | null
B | 1 | 2
B | 2 | 3
B | 3 | 4
B | 4 | 5
db<>fiddle here

SQL Bigquery fill null values with calculated from certain factor

I want to fill null value with a new price value. The new price value will calculated from the other product available price (same product) times the factor.
given table,
Prod | unit | factor | price
abc X 1 24000
abc Y 12 NULL
xyz X 1 NULL
xyz y 5 60000
xyz Z 20 NULL
that formula that comes to mind
null price = avail same prod price * it's factor/null price factor
with the existing table above, examples price formula will be
'abc Y price' = 20000 * 1 / 12 = 2000 (avail price is abc X)
'xyz X price' = 60000 * 5 / 1 = 300000 (avail price is xyz Y)
'xyz Z price' = 60000 * 5 / 20 = 15000 (avail price is xyz Y)
is there any way i can do this?
I think this does what you want:
select t.*,
coalesce(price,
max(price * factor) over (partition by prod) / factor
) as calculated_price
from t;
This replaces NULL prices with the maximum price * factor for the product -- then divided by the factor on the given row.
Below is for BigQuery Standard SQL
if a product has 2 or more price list, just fill the null with the lowest factor
#standardSQL
SELECT t.* REPLACE(IFNULL(t.price, t.factor * p.price / p.factor) AS price)
FROM `project.dataset.table` t
LEFT JOIN (
SELECT prod, ARRAY_AGG(STRUCT(price, factor) ORDER BY factor LIMIT 1)[SAFE_OFFSET(0)].*
FROM `project.dataset.table`
WHERE NOT price IS NULL
GROUP BY prod
) p
USING(prod)
If to apply to sample from your question - result is
Row prod unit factor price
1 abc X 1 24000.0
2 abc Y 12 288000.0
3 xyz X 1 12000.0
4 xyz Y 5 60000.0
5 xyz Z 20 240000.0
Note: it looks like in your formula you need to reverse factors - for example 60000 * 20 / 5 - not sure, but this looks more logical for me. If I am wrong you can adjust t.factor * p.price / p.factor and use p.factor * p.price / t.factor instead
In this case result will be (which matches what you expected but as I said already I suspect is wrong -but it is up to you obviously)
Row prod unit factor price
1 abc X 1 24000.0
2 abc Y 12 2000.0
3 xyz X 1 300000.0
4 xyz Y 5 60000.0
5 xyz Z 20 15000.0

SQL - Return All Possible Combinations of Values in a Column by Means of Two New Columns

I want to return all possible combinations of values in a column by means of two new columns. E.g. my column consists out of the values (A,B,C,D). The possible combinations of those values are (A,B), (A,C), (A,D), (B,C), (B,D), (C,D), (A,B,C), (B,D,C), (D,C,A), (C,A,B) [Remark: I don't want to consider (1) the combintions with just one value, (2) the combination with all values and (3) the combination with no values. Thus I have 2^(n)-n-1-1 combinations for n different values]. I want to list all those combinations in two columns like demonstrated below.
Consider that I start with this column:
Col0
----
A
B
C
D
Out of Col0 I want to produce the 10 combinations using two columns:
Col1 Col2
---- ----
1 A
1 B
2 A
2 C
3 A
3 D
4 B
4 C
5 B
5 D
6 C
6 C
7 A
7 B
7 C
8 B
8 C
8 D
9 C
9 D
9 A
10 D
10 A
10 B
How do I do this in SQL? I use SQLite.
Thank you a lot!
I have a solution, but it requires two changes...
Each item must be given an id (starting from 1)
The output id's may not be sequential
id | datum
----+-------
1 | A
2 | B
3 | C
4 | D
(The output id's I calculate are effectively identifiers for each Permutation, but I don't output permutations you're not interested in...)
group_id | datum
----------+-------
6 | A
6 | B
7 | A
7 | C
8 | A
8 | D
12 | B
12 | C
13 | B
13 | D
18 | C
18 | D
32 | A
32 | B
32 | C
33 | A
33 | B
33 | D
38 | A
38 | C
38 | D
63 | B
63 | C
63 | D
http://dbfiddle.uk/?rdbms=sqlite_3.8&fiddle=87d670ecaba8b735cb3f95fa66cea96b
http://dbfiddle.uk/?rdbms=sqlite_3.8&fiddle=26e4f59874009ef95367d85565563c3c
WITH
cascade AS
(
SELECT
1 AS depth,
NULL AS parent_id,
id,
datum,
id AS datum_id
FROM
sample
UNION ALL
SELECT
parent.depth + 1,
parent.id,
parent.id * (SELECT MAX(id)+1 FROM sample) + child.id - 1,
child.datum,
child.id
FROM
cascade AS parent
INNER JOIN
sample AS child
ON child.id > parent.datum_id
),
travelled AS
(
SELECT
depth AS depth,
parent_id AS parent_id,
id AS group_id,
datum AS datum,
datum_id AS datum_id
FROM
cascade
WHERE
depth NOT IN (1, (SELECT COUNT(*) FROM sample))
UNION ALL
SELECT
parent.depth,
parent.parent_id,
child.group_id,
parent.datum,
parent.datum_id
FROM
travelled AS child
INNER JOIN
cascade AS parent
ON parent.id = child.parent_id
)
SELECT
group_id,
datum
FROM
travelled
ORDER BY
group_id,
datum_id
The first CTE walks all the available combinations (recursively) creating a directed graph. At this stage I don't exclude combinations of one item, or all items, but I do exclude equivalent permutations.
Each node also has a unique identifier calculated for it. There are gaps in these ids, because the calculation would also work for all permutations, even though they're not all included.
Taking any node in that graph and walking up to the final parent node (recursively again) will always give a different combination than if you started from a different node in the graph.
So the second CTE does all of those walks, excluding the combinations of "just one item" and "all items".
The final select just outputs the results in order.
The gaps in the id's are probably avoidable but the maths is too hard for my head at the end of a working day.
The idea is to enumerate the power set, by assigning each value a power of 2, then iterate from 1 to 2^n - 1 , and filter the elements which corresponding bit is set.
-- map each value with a power of 2 : 1, 2, 4, 8, 16
with recursive ELEMENTS(IDX, POW, VAL) as (
-- init with dummy values
values(-1, 0.5, null)
union all
select IDX + 1,
POW * 2,
-- index the ordered values from 0 to N - 1
( select COL0
from DATA d1
where (select count(*) from DATA d2 where d2.COL0 < d1.COL0) = IDX + 1)
from ELEMENTS
where IDX + 1 < (select count(*) from data)
), POWER_SETS(ITER, VAL, POW) as (
select 1, VAL, POW from ELEMENTS where VAL is not null
union all
select ITER + 1, VAL, POW
from POWER_SETS
where ITER < (select SUM(POW) from elements) )
select ITER, VAL from POWER_SETS
-- only if the value's bit is set
where ITER & POW != 0
EDIT: 2nd version, with help from MatBailie. Only one of the CTE is recursive, and singleton subsets are excluded.
WITH RECURSIVE
-- number the values
elements(val, idx) AS (
SELECT d1.col0, (select count(*) from DATA d2 where d2.COL0 < d1.COL0)
FROM DATA d1
),
-- iterate from 3 (1 and 2 are singletons)
-- to 2^n - 1 (subset containing all the elements)
subsets(iter) AS (
VALUES(3)
UNION ALL
SELECT iter + 1
from subsets
WHERE iter < (1 << (SELECT COUNT(*) FROM elements)) - 1
)
SELECT iter AS Col1, val AS Col2
FROM elements
CROSS JOIN subsets
-- the element is present is this subset (the bit is set)
WHERE iter & (1 << idx) != 0
-- exclude singletons (another idea from MatBailie)
AND iter != (iter & -iter)
ORDER BY iter, val
If window functions and CTE is available then you can use the following approach
with data_rn as
(
select d1.col0 col1,
d2.col0 col2,
row_number() over (order by d1.col0) rn
from data d1
inner join data d2 on d1.col0 > d2.col0
)
select rn, col1 from data_rn
union all
select rn, col2 from data_rn
order by rn
dbfiddle demo

how to Get only the rows which's D column hold nearest lowest number to the C column?

------------------------------------------
ID Name C D
------------------------------------------
1 AK-47 10 5
2 RPG 10 20
3 Mp5 20 15
4 Sniper 20 18
5 Tank 90 80
6 Space12 90 20
7 Rifle 90 110
8 Knife 90 85
Consider 1,2 ; 3,4 ; 5,6,7,8 are as separate groups
So i need to get the row group wise that which's D column holds the nearest lower number to the C column
So the Expected Result is :
------------------------------------------
ID Name C D
------------------------------------------
1 AK-47 10 5
4 Sniper 20 18
8 Knife 90 85
How can I achieve this ?
select t1.*
from your_table t1
join
(
select c, min(abs(c-d)) as near
from your_table
group by c
) t2 on t1.c = t2.c and abs(t1.c-t1.d) = t2.near
Here is the syntax for another way of doing this. This uses a cte and will only hit the base table once.
with MySortedData as
(
select ID, Name, C, D, ROW_NUMBER() over(PARTITION BY C order by ABS(C - D)) as RowNum
from Something
)
select *
from MySortedData
where RowNum = 1

SQL - Check for price changes over multiple periods

I am using an MS Access database and am trying to make a query that provides an overview of securities for which the price changed by more than XX% during the last XY consecutive months. I have tried all kind of subqueries but cannot get my head around this.
Please find below a simplified example. The PriceTable contains three attributes: a period, a security id and the price of the security in that period. I am looking for a query that provides me per the last period (in this case 201210) all securities having a price change of more than plus or minus XX% (in this case 3%) in the last XY (in this case 3) months. The three columns on the right hand provide some calculations to further clarify this:
Delta is the price change from one period to the other ((PT-PT-1)/PT-1)
Delta>Threshold: checks whether the change is larger than (plus or minus) 3% (parameter XX)
Counter: checks whether the price change is larger than 3% for 3 (parameter XY) consecutive months
In the example below the query should only show productID number 1.
PriceTable Supporting calculations
+--------+------+-------+--------+-----------------+---------+
+ Period |SecID | Price | Delta% | Delta>Threshold | Counter |
+--------+------+-------+--------+-----------------+---------+
| 201206 | 1 | 105 | 0% | N | 0 |
| 201207 | 1 | 100 | -4.76% | Y | 1 |
| 201208 | 1 | 95 | -5% | Y | 2 |
| 201209 | 1 | 90 | -5.26% | Y | 3 |
| 201210 | 1 | 85 | -5.56% | Y | 4 |
| 201207 | 2 | 95 | 0% | N | 0 |
| 201208 | 2 | 100 | 5.26% | Y | 1 |
| 201209 | 2 | 103 | 3% | N | 0 |
| 201210 | 2 | 99 | -3.88% | Y | 1 |
+--------+------+-------+--------+-----------------+---------+
I hope someone can help me out!
Thanks in advance,
Paul
I don't have Access to hand, but here's a query for SQL Server:
The inner 'h' table is pretty much your helper table. the outer bit joins on 3 periods, and displays if the count with threshold 'Y' is 3
The way I did it you also need functions for working out the next period, and the number of periods between two end points. These should be fairly easy to write in VBA. You could also create a period table with a sequence number to work around this:
-- Function that works out the next period
-- i.e. if you supply 201112, it will return 201201
Create Function dbo.NextPeriod(#Period As Int) Returns Int As
Begin
Declare
#Month int,
#Ret int = Null
If #Period Is Not Null
Begin
Set #Month = #Period - 100 * (#Period / 100)
If #Month < 12
Set #Ret = #Period + 1
Else
Set #Ret = #Period - #Month + 101
End
Return #Ret
End;
-- Function that works out how many periods between the two endpoints
-- dbo.PeriodCount(201112, 201201) = 1
Create Function dbo.PeriodCount(#StartPeriod As Int, #EndPeriod As Int) Returns Int As
Begin
Declare
#StartMonth int,
#EndMonth int,
#StartYear int,
#EndYear int,
#Ret int = Null
If #StartPeriod Is Not Null And #EndPeriod Is Not Null
Begin
Set #StartMonth = #StartPeriod - 100 * (#StartPeriod /100)
Set #StartYear = (#StartPeriod - #StartMonth) / 100
Set #EndMonth = #EndPeriod - 100 * (#EndPeriod / 100)
Set #EndYear = (#EndPeriod - #EndMonth) / 100
Set #Ret = (12 * #EndYear + #EndMonth) - (12 * #StartYear + #StartMonth)
End
Return #Ret
End;
-- Show periods that are the start of a run
-- of #Periods periods with threshold
-- of at least #Threshold
Declare #Threshold Decimal(10, 2) = 3
Declare #Periods int = 3
Select
p0.SecurityID,
p0.Period
From
PriceTable p0
Inner Join (
Select
p1.*,
100 * (p1.Price - p2.Price) / p2.Price As Delta,
Case When Abs(100 * (p1.Price - p2.Price) / p2.Price) > #Threshold Then 'Y' Else 'N' End As OverThreshold
From
PriceTable p1
Left Outer Join
PriceTable p2
On p1.SecurityID = p2.SecurityID And
p1.Period = dbo.NextPeriod(p2.Period)
) h
On p0.SecurityID = h.SecurityID And
dbo.PeriodCount(p0.Period, h.Period) Between 0 And (#Periods - 1) And
h.OverThreshold = 'Y'
Group By
p0.SecurityID,
p0.Period
Having
Count(*) = #Periods
Order By
p0.SecurityID,
p0.Period;
This shows you how the method works, you can simplify it like so:
Declare #Threshold Decimal(10, 2) = 3
Declare #Periods int = 3
Select
p0.SecurityID,
p0.Period
From
PriceTable p0
Inner Join
PriceTable p1
On p0.SecurityID = p1.SecurityID And
dbo.PeriodCount(p0.Period, p1.Period) Between 0 And (#Periods - 1)
Inner Join
PriceTable p2
On p1.SecurityID = p2.SecurityID And
p1.Period = dbo.NextPeriod(p2.Period)
Where
Abs(100 * (p1.Price - p2.Price) / p2.Price) > #Threshold
Group By
p0.SecurityID,
p0.Period
Having
Count(*) = #Periods
Order By
p0.SecurityID,
p0.Period;
http://sqlfiddle.com/#!3/8eff9/2
#Laurence: please find below the code
Public Function NextPer(Nperiod As Long) As Long
Dim Month As Long
If Not IsNull(Nperiod) Then
Month = 100 * ((Nperiod / 100) - Round(Nperiod / 100, 0))
If Month < 12 Then
NextPer = Nperiod + 1
Else
NextPer = Nperiod - Month + 101
End If
End If
End Function
Public Function PCount(SPeriod As Long, EPeriod As Long) As Long
Dim SMonth As Long
Dim EMonth As Long
Dim SYear As Long
Dim EYear As Long
If Not IsNull(SPeriod) And Not IsNull(EPeriod) Then
SMonth = 100 * ((SPeriod / 100) - Round(SPeriod / 100, 0))
SYear = (SPeriod - SMonth) / 100
EMonth = 100 * ((EPeriod / 100) - Round(EPeriod / 100, 0))
EYear = (EPeriod - EMonth) / 100
PCount = (12 * EYear + EMonth) - (12 * SYear + SMonth)
End If
End Function
And the QUERY (the parameters are for the moment hardcoded)
SELECT p0.SecurityID, p0.Period
FROM (PriceTable AS p0
INNER JOIN PriceTable AS p1 ON (p0.SecurityID = p1.SecurityID)
AND (PCount(p0.Period,p1.Period)>=0) AND (PCount(p0.Period,p1.Period)<=2))
INNER JOIN PriceTable AS p2 ON (p1.SecurityID = p2.SecurityID)
AND (p1.Period = NextPer(p2.Period))
WHERE Abs(100*(p1.Price-p2.Price)/p2.Price)>0.03
GROUP BY p0.SecurityID, p0.Period
HAVING Count(*) = 3
ORDER BY p0.SecurityID asc , p0.Period asc;
+1 for your intention of trying to get this in query itself without UDFs. Out of extreme interest I have put some effort to find a solution. I admit following code is not the most efficient code. (with all those IIFs, the performance is not that great)
Getting first 5 columns as per your above table are pretty straightforwad. I have saved that in qryDelta. I find the tricky part of the question is to have Counter in the same results table. Second query qryCounter will give you the final table as you expected.
qryDelta
SELECT a.period, a.secid, a.price,
iif(isnull(ROUND((a.price-b.price)/b.price*100,2)),0,
ROUND((a.price-b.price)/b.price*100,2)) AS Delta,
iif(abs((a.price-b.price)/b.price)*100>3,"Y","N") AS Threshold,
SUM(iif(abs((a.price-b.price)/b.price)*100>3,1,0)) AS [Counter]
FROM tbldelta AS a LEFT JOIN tbldelta AS b
ON (a.secid = b.secid) AND (a.period = b.period + 1)
GROUP BY a.period, a.secid, a.price,
iif(isnull(ROUND((a.price-b.price)/b.price*100,2)),0,
ROUND((a.price-b.price)/b.price*100,2)),
iif(abs((a.price-b.price)/b.price)*100>3,"Y","N")
ORDER BY a.secid, a.period;
Results:
qryCounter
SELECT q.period, q.secid, q.price, q.delta, q.threshold,
SUM(iif(q.counter=0,0,1)) AS Counter
FROM qryDelta q
LEFT JOIN tblDelta t
ON q.secid = t.secid
AND (t.period < q.period)
GROUP BY q.secid, q.period, q.price, q.delta, q.threshold
Results:
However I too faced the issue with SecId = 2, Period = 201208 with a total = 2. So I changed my query conditions. Now the results seem to show the cumulative periodic count properly except for SectID = 2, Period = 201210 total = 3. Perhpas you guys could throw some light to this. Out of most of the experiments done, it seems more or less a bug on JOIN and between dates that we are trying to put as coditions here.
PS:
If you have decided to build user defined functions (UDF), then you may consider two things. Are you using Excel as front end or Access as front end. Then you have to provide necessary arrangements to call your Access UDF & query from Excel. If you are only using Access as both front and back end, then ofcourse using a UDF would be much easier to handle.
I solved it using just SQL. Here's how I did.
First of all, we need a query that, for each rows, shows the distance in rows from the last period:
Period SecID Price Row
===============================
201206 1 105 4
201207 1 100 3
201208 1 95 2
201209 1 90 1
201210 1 85 0
201207 2 95 3
201208 2 100 2
201209 2 103 1
201210 2 99 0
we will call it PriceTable_Ordered:
SELECT
PriceTable.Period,
PriceTable.SecID,
PriceTable.Price,
(select count(*) from PriceTable PriceTable_1
where PriceTable_1.SecID = PriceTable.SecID
AND PriceTable_1.Period > PriceTable.Period) AS Row
FROM PriceTable;
Now to calculate the Delta, and showing if the Delta is more than the threesold, we can use this query that we will call PriceTable_Total1:
SELECT
PriceTable_Ordered.*,
PriceTable_Ordered_1.Price,
(PriceTable_Ordered.Price-PriceTable_Ordered_1.Price)/(PriceTable_Ordered_1.Price) AS Delta,
iif((ABS(Delta*100)>3),"Y","N") AS DeltaThreesold
FROM
PriceTable_Ordered LEFT JOIN PriceTable_Ordered AS PriceTable_Ordered_1
ON (PriceTable_Ordered.SecID = PriceTable_Ordered_1.SecID)
AND (PriceTable_Ordered.[Row]=PriceTable_Ordered_1.[Row]-1);
And this returns:
Period SecID Price1 Row Price2 Delta DeltaThreesold
=========================================================
201206 1 105 4 N
201207 1 100 3 105 -4,76 Y
201208 1 95 2 100 -0,05 Y
201209 1 90 1 95 -5,26 Y
201210 1 85 0 90 -5,55 Y
201207 2 95 3 N
201208 2 100 2 95 5,26 Y
201209 2 103 1 100 0,03 N
201210 2 99 0 103 -3,88 Y
Now we can create PriceTable_Total2 based on PriceTable_Total1:
SELECT
PriceTable_Total1.Period,
PriceTable_Total1.SecID,
PriceTable_Total1.PriceTable_Ordered.Price,
PriceTable_Total1.Delta,
PriceTable_Total1.DeltaThreesold,
PriceTable_Total1.Row,
(select min(row) from PriceTable_Total1 PriceTable_Total1_1
where PriceTable_Total1.SecID = PriceTable_Total1_1.SecId
and PriceTable_Total1.Row < PriceTable_Total1_1.Row
and PriceTable_Total1_1.DeltaThreesold="N") AS MinN,
IIf([DeltaThreesold]="Y",[MinN]-[row],0) AS CountRows
FROM PriceTable_Total1;
we select all the columns of PriceTable_Total1, then for each row we count the minimum row number > than current row where threesold is "N". If current row is over threesold, the count we need is just this difference, otherwise it's 0. Here's the result:
Period SecID Price Delta DelTh Row MinN CountRows
========================================================
201206 1 105 N 4 0
201207 1 100 -4,76 Y 3 4 1
201208 1 95 -0,05 Y 2 4 2
201209 1 90 -5,26 Y 1 4 3
201210 1 85 -5,55 Y 0 4 4
201207 2 95 N 3 0
201208 2 100 5,26 Y 2 3 1
201209 2 103 0,03 N 1 3 0
201210 2 99 -3,88 Y 0 1 1
You can then hide the columns that you don't need. This query should work even if we cross the year and even if some periods are missing.
SELECT PriceTable_Total2.Period, PriceTable_Total2.SecID
FROM PriceTable_Total2
WHERE (PriceTable_Total2.Period=
(select max(period)
from PriceTable
where PriceTable.SecID=PriceTable_Total2.SecID)
AND (PriceTable_Total2.[CountRows])>=3);
this will return:
Period SecID
201210 1
and that means that only SecID 1 is over threesold in the last period for more than 3 months.
I hope this answer is correct, it was nice to try to solve it!!