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