【问题标题】:Compare date ranges with specific dates when building a query构建查询时将日期范围与特定日期进行比较
【发布时间】:2013-10-21 10:40:51
【问题描述】:

我已经为这个问题苦恼了三天,也许你可以帮我解决这个问题。

所以,我有 3 张桌子。

一个称为商品价格,其中包含一些商品在给定时间内的价格。

表格基本上是这样的:

ItemName | Startdate  | Enddate    | Price
-------------------------------------------
Watch    | 01/01/2012 | 04/08/2012 | 180€
Watch    | 05/08/2012 | 31/12/2012 | 150€
Watch    | 01/01/2013 | 31/12/2013 | 200€

另一个表名为Sales,其中包含每个月售出的商品数量。

表格如下所示:

ItemName | SaleMonth  | Number
-----------------------------
Watch    | 01/01/2012 | 93
  .             .        .
  .             .        .
  .             .        .
Watch    | 01/05/2013 | 59
Watch    | 01/06/2013 | 74

最后一个叫做汇率,每个月从欧元到英镑的汇率。

ExchangeMonth | Rate  |
-----------------------
 01/01/2013   | 1.225 | 
 01/02/2013   | 1.166 |

为了计算我在一个月内获得了多少,我使用这个公式:

Gain_month_i = (ItemPrice_month_i-1 * ItemsSold_month_i-1)/ExchangeRate_month_i

我想计算在给定时期内我获得了多少。

但是,如果我选择的时间段与我的项目价格表中的不同时间段重叠(例如,如果我想查看 01/02/ 2012 年和 2014 年 1 月 1 日)

【问题讨论】:

    标签: sql ms-access


    【解决方案1】:

    您的问题的本质是您的 [Sales] 数字是按月汇总的,但商品的价格可能在月初和月底之间发生了变化。所以,如果你在 2012 年 8 月卖出了 50 只手表,你并不确定价格是 180 时卖出了多少,价格下降到 150 后卖出了多少。

    在我看来,唯一合理的解决方案是使用基于一个月中各种价格有效的天数的平均每月价格。您可以通过使用这样的 VBA 函数来实现:

    Option Compare Database
    Option Explicit
    
    Public Function AverageMonthlyPrice(ItemName As String, AnyDayInMonth As Date) As Variant
    Dim qdf As DAO.QueryDef, rst As DAO.Recordset
    Dim FirstDayOfMonth As Date, LastDayOfMonth As Date, NumDaysInMonth As Long
    Dim IntervalStart As Date, IntervalEnd As Date
    Dim WeightedAveragePrice As Variant
    
    WeightedAveragePrice = Null
    FirstDayOfMonth = DateSerial(year(AnyDayInMonth), Month(AnyDayInMonth), 1)
    LastDayOfMonth = DateAdd("d", -1, DateSerial(year(AnyDayInMonth), Month(AnyDayInMonth) + 1, 1))
    NumDaysInMonth = DateDiff("d", FirstDayOfMonth, LastDayOfMonth) + 1
    Set qdf = CurrentDb.CreateQueryDef("", _
            "SELECT * FROM [Item Price] " & _
            "WHERE ItemName = [pItemName] " & _
                "AND Enddate >= [pFirstDay] AND Startdate <= [pLastDay]")
    qdf!pItemName = ItemName
    qdf!pFirstDay = FirstDayOfMonth
    qdf!pLastDay = LastDayOfMonth
    Set rst = qdf.OpenRecordset(dbOpenSnapshot)
    Do While Not rst.EOF
        IntervalStart = IIf(rst!StartDate < FirstDayOfMonth, FirstDayOfMonth, rst!StartDate)
        IntervalEnd = IIf(rst!EndDate > LastDayOfMonth, LastDayOfMonth, rst!EndDate)
        WeightedAveragePrice = Nz(WeightedAveragePrice, 0) + rst!Price * (DateDiff("d", IntervalStart, IntervalEnd) + 1) / NumDaysInMonth
        rst.MoveNext
    Loop
    rst.Close
    Set rst = Nothing
    Set qdf = Nothing
    If Not IsNull(WeightedAveragePrice) Then
        WeightedAveragePrice = CCur(WeightedAveragePrice)
    End If
    AverageMonthlyPrice = WeightedAveragePrice
    End Function
    

    因此,现在您可以在 Access 中运行查询,将 [Sales] 表中每一行的销售数量、平均价格和汇率汇总在一起:

    SELECT
        Sales.ItemName,
        Sales.SaleMonth,
        Sales.Number,
        AverageMonthlyPrice(Sales.ItemName, Sales.SaleMonth) AS AveragePrice,
        [Exchange Rate].Rate AS ExchangeRate
    FROM
        Sales
        INNER JOIN
        [Exchange Rate]
            ON [Exchange Rate].ExchangeMonth = Sales.SaleMonth
    

    ...返回类似:

    ItemName  SaleMonth   Number  AveragePrice  ExchangeRate
    --------  ----------  ------  ------------  ------------
    Watch     2012-01-01      93  180                  1.225
    Watch     2012-08-01      50  153.871              1.166
    Watch     2013-06-01      74  200                  1.234
    Watch     2013-05-01      59  200                  1.123
    

    如果将该查询保存在 Access 中,则可以在其他查​​询中使用它(就像使用表一样)来执行其余的计算。

    【讨论】:

    • 感谢您的回答。如果我理解得很好,这个功能会在当月没有价格变化的情况下给出正常价格,或者如果发生这种情况,则按比例计算。因此,如果我想用我的公式执行我的计算,我只需要执行以下操作:SELECT SUM((AverageMonthlyPrice(Sales.ItemName, Sales.SaleMonth)*Sales.Number)/Exchangerate.Rate) AS Gain FROM Sales, [Exchange rate] WHERE Sales.Itemname="Watch" AND Sales.SaleMonth BETWEEN DATEADD("m", -1,01/02/2012) AND DATEADD("m", -1, 31/12/2013) AND [Exchangerate].Exchangemonth = DATEADD("m", 1, Sales.SaleMonth); 我对吗?
    • @Tom 是的,如果该月没有价格变化,该函数应该只给出“正常”价格。至于您的查询,为什么不对一小部分数据进行尝试,看看结果是否与您自己“做数学”时得到的结果相匹配?
    • 我将其标记为好答案,因为我认为这是我的本意,但我还有一个问题。这只是我的问题的一个简单示例,我的表有点复杂,例如 Sales.Itemname 不等于 [Item price].Itemname 但两个表由另一个表链接。函数中需要修改哪些字段以匹配具有不同字段但基本上字段包含相同数据的表?
    • @Tom 您可能根本不需要更改函数。我的答案末尾的示例查询将 Sales.ItemName 值传递给函数,但您的查询可以很容易地将某个其他表中的值传递给它,该表将 Sales.ItemName 值映射到 [Item Price].ItemName 值。 (该映射可能通过在查询中执行 JOIN 来实现。)换句话说,该函数不知道(或关心)它的 ItemName 参数来自哪里,它只需要是一个与项目匹配的字符串[Item Price] 表中的名称。
    • 谢谢它的工作,我只是在你的代码中添加了一些变量名更改以匹配我的表。
    猜你喜欢
    • 1970-01-01
    • 2017-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多