SQL 将库存计数表连接到日期表

Posted

技术标签:

【中文标题】SQL 将库存计数表连接到日期表【英文标题】:SQL Join Inventory Count Table to Date table 【发布时间】:2014-06-24 06:06:58 【问题描述】:

我有一个不同产品的运行库存表,记录每次交易后的库存计数。事务并非每天都发生,因此该表没有每日运行计数。

我需要列出每个产品的所有日期,以便我可以对一段时间内的计数求和并求平均值。

库存

DATE        ID  Qty Count
2014-05-13  123 12  12
2014-05-19  123 -1  11
2014-05-28  123 -1  10
2014-05-29  123 -3  7
2014-05-10  124 5   5
2014-05-15  124 -1  4
2014-05-21  124 -1  3
2014-05-23  124 -3  0

我有一个包含加入日期的表格,但我不确定如何将缺失的日期加入多个产品。

我需要如下查询。它需要返回所选时间段内的计数,但还包括其间的日期。

DATE        ID  Qty Count
2013-05-01  123 0   0
2013-05-02  123 0   0
2013-05-03  123 0   0
2013-05-04  123 0   0
2013-05-05  123 0   0
2013-05-06  123 0   0
2013-05-07  123 0   0
2013-05-08  123 0   0
2013-05-09  123 0   0
2013-05-10  123 0   0
2013-05-11  123 0   0
2013-05-12  123 0   0
2014-05-13  123 12  12
2013-05-14  123 0   12
2013-05-15  123 0   12
2013-05-16  123 0   12
2013-05-17  123 0   12
2013-05-18  123 0   12
2014-05-19  123 -1  11
2013-05-20  123 0   11
2013-05-21  123 0   11
2013-05-22  123 0   11
2013-05-23  123 0   11
2013-05-24  123 0   11
2013-05-25  123 0   11
2013-05-26  123 0   11
2013-05-27  123 0   11
2014-05-28  123 -1  10
2014-05-29  123 -3  7
2013-05-30  123 0   7
2013-05-31  123 0   7
2013-05-01  124 0   0
2013-05-02  124 0   0
2013-05-03  124 0   0
2013-05-04  124 0   0
2013-05-05  124 0   0
2013-05-06  124 0   0
2013-05-07  124 0   0
2013-05-08  124 0   0
2013-05-09  124 0   0
2014-05-10  124 5   5
2014-05-11  124 0   5
2014-05-12  124 0   5
2014-05-13  124 0   5
2014-05-14  124 0   5
2014-05-15  124 -1  4
2014-05-16  124 0   4
2014-05-17  124 0   4
2014-05-18  124 0   4
2014-05-19  124 0   4
2014-05-20  124 0   4
2014-05-21  124 -1  3
2014-05-22  124 0   3
2014-05-23  124 -3  0
2014-05-24  124 0   0
2014-05-25  124 0   0
2014-05-26  124 0   0
2014-05-27  124 0   0
2014-05-28  124 0   0
2014-05-29  124 0   0
2014-05-30  124 0   0
2014-05-31  124 0   0

【问题讨论】:

到目前为止你试过了吗? 【参考方案1】:

使用inv join inv 构建至少 31 行并构建一个 31 天的表。然后加入ids,最后加入原表。

select a.d, a.id, a.qty,
  if(a.id=@lastid, @count:=@count+a.qty, @count:=a.count) `count`,
  @lastid:=a.id _lastid
from (
  select a.d, b.id, ifnull(c.qty, 0) qty, ifnull(c.count, 0) `count`
  from (
    select adddate('2014-05-01', @row) d, @row:=@row+1 i
    from inv a
    join inv b
    join (select @row := 0) c
    limit 31) a
  join (
    select distinct id
    from inv) b
  left join inv c on a.d = c.date and b.id = c.id
  order by b.id, a.d) a
join (select @count := 0, @lastid := 0) b;

fiddle

【讨论】:

这看起来可行。而且我应该能够选择过日期并使用子日期来确定限制数的范围。它需要按日期和 id 排序,然后对计数列求和才能正常工作,但总的来说,这似乎可行。我将尝试使用上述编辑将其添加到我的完整表格中,看看它是如何工作的。 @Cascades,哎呀。没有意识到count 显示累积值。已更正。 这帮助了很多并且基本上有效。谢谢你。我的编辑是我最终使用现有表来创建日期行而不是交叉连接。对于我使用的产品数量,交叉连接的性能很差。【参考方案2】:

以下是所需的步骤:

    获取两个给定日期之间的所有日期。 获取每个 ID 的初始库存。这是:获取该 ID 的给定开始日期或之后的第一个日期,读取此记录的库存并减去其交易数量。 获取每个日期的上一个库存。如果有该日期的记录,则添加其交易数量并将结果与​​其库存数量进行比较。如果值不匹配,则抛出错误。 (这是因为你存储的数据是冗余的,一条记录的数量必须等于上一条记录的数量加上它自己的交易数量。但是数据总是不一致的,所以最好检查一下。)显示新库存和与之前库存的差异.

所有这一切通常可以通过日期的递归 CTE、使用 KEEP DENSE_RANK 函数为所有初始股票的派生表以及查看先前记录的 LAG 函数来实现。

mysql 不支持递归 CTE,或者根本不支持 CTE。您可以使用足够大的表和变量来模拟这一点。 MySQL 不支持 KEEP DENSE_RANK 函数。您可以改为使用另一个派生表来首先找到每个 ID 的最短日期。 MySQL 不支持 LAG 功能。您可以改为在 MySQL 中使用变量。

话虽如此,我建议改用编程语言(Java、C#、php 等)。您只需使用 SQL 选择原始数据,使用循环并简单地在每个记录库上进行所有处理。这比构建一个非常复杂的查询来完成所有需要的查询要方便得多(并且可读性强)。你可以在 SQL 甚至 MySQL 中做到这一点;我只是不推荐它。

【讨论】:

【参考方案3】:

我最终用来解决这个问题的 SQL 结合了@Fabricators 的答案(这确实是一个正确的答案)和我的编辑。

我最终使用现有表来创建日期行,而不是交叉连接。对于我使用的产品数量,交叉连接的性能很差。

SELECT
POSTDATE,
IF(@PROD_ID = PRODUCT_ID, @NEW := 0, @NEW := 1) AS New_Product,
(@PROD_ID := PRODUCT_ID) AS PRODUCT_ID,
QUANTITY,
IF(@NEW = 1, @INVENTORY := QUANTITY, @INVENTORY := @INVENTORY+QUANTITY) AS 'Count'
FROM (
    (
        SELECT
        POSTDATE,
        PRODUCT_ID,
        QUANTITY
        FROM
        inventory
    )
    UNION ALL
    (
        SELECT
        dateslist_sub.TransDate AS POSTDATE,
        productlist_sub.PRODUCT_ID,
        0 AS QUANTITY,
        FROM
        (
            SELECT
            TransDate
            FROM
            (
                SELECT
                adddate('2013-05-01', @row) AS TransDate,
                @row:=@row+1 i
                FROM
                any_table,
                (SELECT @row := 0) row
            ) datestable
            WHERE
            TransDate <= CURDATE()
        ) dateslist_sub
        cross join (
            SELECT
            PRODUCT_ID
            FROM
            products_table
            ORDER BY
            PRODUCT_ID ASC
        ) productlist_sub
        ORDER BY
        productlist_sub.PRODUCT_ID ASC,
        dateslist_sub.TransDate ASC
    )
    ORDER BY
    PRODUCT_ID ASC,
    POSTDATE ASC
) daily_rows_sub

【讨论】:

这可能更适合作为 UNION DISTINCT 或 RIGHT INNER JOIN 以减少最终查询中重复的 POSTDATE。

以上是关于SQL 将库存计数表连接到日期表的主要内容,如果未能解决你的问题,请参考以下文章

使用 VBA 将 Excel 表连接到 SQL Server

What Joins 用于将多个表连接到一个视图中

将新列从另一个表连接到 redshift/sql 表

如何将学生和评估表连接到一个视图中?

mysql - 如何使用父表中的值将连接表连接到另一个连接表?

Hibernate:如何在注释中将三个 3 表连接到一个连接表中?