我正在使用MS Access数据库,并且正在尝试进行查询,以提供在过去XY连续月份内价格变化超过XX%的证券概览。我已经尝试过所有类型的子查询,但无法理解这一点。
请在下面找到一个简化的例子。 PriceTable包含三个属性:期间,安全ID和该期间的证券价格。我正在寻找一个查询,为我提供最后一个期间(在本例中为201210)所有证券在最后一个XY(在这种情况下为3)的价格变化超过正负XX%(在这种情况下为3%)个月。右边的三列提供了一些计算,以进一步澄清这一点:
Delta是从一个时期到另一个时期的价格变化((PT-PT-1)/ PT-1)
Delta> Threshold:检查更改是否大于(加或减)3%(参数XX)
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 |
+--------+------+-------+--------+-----------------+---------+
我希望有人可以帮助我!
提前致谢,
保
答案 0 :(得分:1)
我没有Access,但这是SQL Server的查询: 内部'h'表几乎是你的助手表。外部位连接3个周期,并显示阈值为“Y”的计数是否为3 我这样做的方式你还需要用于计算下一个时期的函数,以及两个终点之间的时段数。这些应该很容易用VBA编写。您还可以创建一个带有序列号的句号表来解决此问题:
-- 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;
这将向您展示该方法的工作原理,您可以这样简化:
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;
答案 1 :(得分:1)
@Laurence:请在下面找到代码
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;
答案 2 :(得分:1)
+1表示您打算在没有UDF的情况下尝试在查询中获取此信息。出于极大的兴趣,我已经付出了一些努力来寻找解决方案。我承认以下代码不是最有效的代码。 (对于所有那些IIF,表现并不是那么好)
根据上表获得前5列非常简单。我在qryDelta中保存了这个。我发现问题的棘手部分是将Counter放在同一个结果表中。第二个查询qryCounter将按照您的预期为您提供最终表格。
<强> 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;
结果:
<强> 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
结果:
但是我也遇到了SecId = 2,Period = 201208,总数= 2的问题。所以我改变了查询条件。现在结果似乎正确显示了累积的周期性计数,除了SectID = 2,Period = 201210 total = 3. Perhpas你们可以为此提供一些启示。在大多数完成的实验中,似乎或多或少是JOIN和日期之间的错误,我们试图将其作为编码放在这里。
PS: 如果您已决定构建用户定义的函数(UDF),那么您可以考虑两件事。您是使用Excel作为前端还是Access作为前端。然后你必须提供必要的安排来调用你的Access UDF&amp;从Excel查询。如果您只使用Access作为前端和后端,那么使用UDF会更容易处理。
答案 3 :(得分:1)
我只使用SQL解决了它。这是我的方式。
首先,我们需要一个查询,对于每一行,显示距离上一个句点的行距离:
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
我们将其称为 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;
现在计算Delta,并显示Delta是否超过三倍,我们可以使用此查询,我们将调用 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);
然后返回:
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
现在我们可以根据PriceTable_Total1创建 PriceTable_Total2 :
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;
我们选择PriceTable_Total1的所有列,然后对于每一行,我们计算minimum row number > than current row
,其中三倍数为“N”。如果当前行超过三倍,我们需要的计数就是这个差异,否则它是0.这是结果:
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
然后,您可以隐藏不需要的列。即使我们跨越一年,即使缺少某些时期,此查询也应该有效。
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);
这将返回:
Period SecID
201210 1
这意味着只有SecID 1在过去一段时间内超过三个月才超过3个月。
我希望这个答案是正确的,尝试解决它很好!!