【问题标题】:MySQL get point in time totals from related tablesMySQL 从相关表中获取时间点总计
【发布时间】:2011-01-28 17:10:38
【问题描述】:

我们有一个订单簿和发票系统,我的任务是尝试从这些表中输出每月滚动总计。
但我真的不知道从哪里开始。我认为有一些我什至还不知道的 SQL 语法。我熟悉 INNER/LEFT/JOINS 和 GROUP BY 等,但按日期分组令人困惑,因为我不知道如何将数据限制在当时分组的当前日期。我认为这将涉及将表连接到它们自己或可能是子选择。我一直认为最好避免在绝对必要时进行子选择。

系统基本上有3个表
订单:order_id、货币、order_stamp
orders_lines:order_line_id、invoice_id、order_id、价格
发票:invoice_id、invoice_stamp
order_stamp 和 invoice_stamp 是存储为整数的 UTC unix 时间戳,而不是 MySQL 时间戳。

我正在尝试按年/月列出在该时间点显示当前未开票订单总数(价格总和)的列表。
当前订单是 order_stamp 小于或等于当月 1 日 00:00 的订单。
未开票的订单是 invoice_stamp 为空或 invoice_stamp 大于当月 1 日 00:00 的订单。
那时可能还没有相关发票,并且 invoice_id 可能为空。

有人对我应该加入什么以及我需要分组有什么建议吗?

干杯,B

【问题讨论】:

  • 这里是滚动总计的一个很好的例子,可以帮助您入门。 dev.mysql.com/tech-resources/articles/…
  • 有趣的是,我已经通读了该内容,但在尝试将其应用于我的情况时感到头疼。这是一个示例查询,应该获取我想要的一个月的数据: SELECT SUM(orders_lines.price) FROM orders_lines LEFT JOIN orders USING(order_id) LEFT JOIN invoices ON @987654328 @.invoice_id=invoices.invoice_id WHERE orders.order_stamp orders_lines.invoice_id 为空或invoices.`invoice_stamp > UNIX_TIMESTAMP('yyyy-mm-01 00:00:00') ) 其中 yyyy-mm 是来自父查询的当前月份

标签: mysql


【解决方案1】:
SELECT  yr, mon, COALESCE(SUM(price), 0)
FROM    (
        SELECT  2010 AS yr
        UNION ALL
        SELECT  2009
        UNION ALL
        …
        ) as years
CROSS JOIN
        (
        SELECT  1 AS mon
        UNION ALL
        SELECT  2
        …
        UNION ALL
        SELECT  12
        ) as months
LEFT JOIN
        orders o
ON      o.order_stamp <= UNIX_TIMESTAMP(CAST(CONCAT(year, mon, '01') AS DATE))
LEFT JOIN
        order_lines ol
ON      ol.order_id = o.order_id
        AND NOT EXISTS
        (
        SELECT  NULL
        FROM    invoice i
        WHERE   i.invoice_id = ol.invoice_id
                AND i.invoice_stamp > UNIX_TIMESTAMP(CAST(CONCAT(year, mon, '01') AS DATE))
        )
GROUP BY
        yr, mon

【讨论】:

    【解决方案2】:

    您的架构中的日期让我有点混乱......订单印章是下订单的UTC,对吗?而 invoice_stamp 是为客户开具发票时的 UTC?

    我不确定您所说的“滚动每月总计”是什么意思,所以我假设它是给定月份所有发票的集合?以及给定月份所有订单的集合?

    未开票的客户是没有发票的客户...

    如果是这样,类似:

       SELECT YEAR(FROM_UNIXTIME(order_stamp)), 
              MONTH(FROM_UNIXTIME(order_stamp)), 
              SUM(price)
       FROM orders
          LEFT JOIN order_lines USING (order_id)
          LEFT JOIN invoices USING (invoice_id)
       WHERE invoices.invoice_id IS NULL
       GROUP BY YEAR(FROM_UNIXTIME(order_stamp)), 
                MONTH(FROM_UNIXTIME(order_stamp))
    

    ...应该可以带你到那里。

    如果您正在寻找超过 30 天或其他时间的物品的收款费用,您需要以下内容:

    SELECT YEAR(FROM_UNIXTIME(invoice_date)), 
           MONTH(FROM_UNIXTIME(invoice_date)), 
           SUM(OverThirtyDayPrice),
           AVG(CollectionDays)
    FROM 
        (
         SELECT invoice_date,
                IF(
                   DATEDIFF(
                            FROM_UNIXTIME(IFNULL(invoice_stamp, UNIX_TIMESTAMP()), 
                            order_stamp) > 30, 
                   price, 
                   0
                  ) AS OverThirtyDayPrice,
                DATEDIFF(FROM_UNIXTIME(IFNULL(invoice_stamp, UNIX_TIMESTAMP()),
                         order_stamp)) AS CollectionDays
         FROM orders
             LEFT JOIN order_lines USING (order_id)
             LEFT JOIN invoices USING (invoice_id)
        )
    GROUP BY YEAR(FROM_UNIXTIME(invoice_date)), 
             MONTH(FROM_UNIXTIME(invoice_date))
    

    我们可以稍微修改一下最后一个,使其适用于在一个月内订购但在另一个月内计费的东西:

    SELECT YEAR(invoice_datetime), 
           MONTH(invoice_datetime), 
           SUM(OutOfMonthPrice)
    FROM 
        (
         SELECT FROM_UNIXTIME(IFNULL(invoice_stamp, UNIX_TIMESTAMP())) AS invoice_datetime,
                IF(MONTH(
                         FROM_UNIXTIME(IFNULL(invoice_stamp, UNIX_TIMESTAMP()))
                        ) 
                   <> 
                   MONTH(FROM_UNIXTIME(order_stamp)),
                   price, 
                   0
                  ) AS OutOfMonthPrice,
         FROM orders
             LEFT JOIN order_lines USING (order_id)
             LEFT JOIN invoices USING (invoice_id)
        )
    GROUP BY MONTH(invoice_datetime), YEAR(invoice_datetime)
    

    如果您想获得“时间点”查询,您可能会发现 RDBMS 不是您所需要的(您需要迭代一系列查询,每个查询针对特定的月/年) 和/或您的基本设计不支持您尝试生成的报告。

    理想情况下,您需要的是存档和报告的月度报告。

    【讨论】:

    • 感谢您的建议。这让我思考,但我不确定我是否正确解释了这一点。以你的最后一个例子为例,我认为它可能很接近。基本上,我正在寻找在那个特定时间点尚未开具发票的所有订单的价值。因此,对于每个月,它都需要排除未来的订单(其中 order_stamp 大于该月 1 日的 00:00)。并且还排除已经开票但包括将来会开票的订单。使困惑?我是!
    • 但是用你的第三个例子我想我可以通过在 FROM() 子句中提供子选择来解释它...... SELECT SUM(orders_lines.price) FROM orders_lines LEFT JOIN orders USING (order_id) LEFT JOIN invoices ON orders_lines.invoice_id=invoices.invoice_id WHERE orders.order_stamp invoice_id IS NULL OR invoices.invoice_stamp > UNIX_TIMESTAMP('yyyy-mm-01 00:00:00') ) 其中 yyyy-mm 是当前月份的年/月" 查询正在循环。不知道如何将其传递给子选择
    • @batfastad:我肯定很困惑。哈哈!而 cmets 很讨厌改变事情,所以我会用更多细节来编辑我的原始答案。不过,我认为您可能超出了这个问题的范围:您正在描述一份非常奇怪的报告,该报告不符合我所知道的任何业务模式。