SQL Server:检测库存变化

Posted

技术标签:

【中文标题】SQL Server:检测库存变化【英文标题】:SQL Server: detect inventory changes 【发布时间】:2018-04-29 16:53:01 【问题描述】:

我有一个简单的库存表:

IF OBJECT_ID('tempdb.dbo.#t') IS NOT NULL
    DROP TABLE #t
GO

CREATE TABLE #t
(
    [date] DATE,
    Item VARCHAR(1),
    [Location] INT,
    Qty INT
)

INSERT INTO #t ([date], [Item], [Location], [Qty]) 
VALUES ('2017-11-16', 'A', 1, 5),
       ('2017-11-16', 'B', 1, 5),
       ('2017-11-16', 'B', 2, 10),
       ('2017-11-16', 'A', 3, 1),
       ('2017-11-16', 'C', 3, 2),
       ('2017-11-16', 'A', 4, 20),
       ('2017-11-15', 'A', 1, 5),
       ('2017-11-15', 'B', 1, 5),
       ('2017-11-15', 'B', 2, 10),
       ('2017-11-15', 'A', 3, 1),
       ('2017-11-15', 'C', 3, 8),
       ('2017-11-14', 'A', 1, 10),
       ('2017-11-14', 'B', 1, 1),
       ('2017-11-14', 'B', 2, 10),
       ('2017-11-14', 'A', 3, 1),
       ('2017-11-14', 'C', 3, 8)

我想找出日期(在 where 子句中)以及过去位置项目级别的数量差异。

因此结果应该如下:

+------------+------+----------+-----+------------+---------+
|    Date    | Item | Location | Qty | LastChange | LastQty |
+------------+------+----------+-----+------------+---------+
| 16.11.2017 | A    |        1 |   5 | 14.11.2017 |      10 |
| 16.11.2017 | B    |        1 |   5 | 14.11.2017 |       1 |
| 16.11.2017 | B    |        2 |  10 |            |         |
| 16.11.2017 | A    |        3 |   1 |            |         |
| 16.11.2017 | C    |        3 |   2 | 15.11.2017 |       8 |
| 16.11.2017 | A    |        4 |  20 |            |         |
+------------+------+----------+-----+------------+---------+

由于库存表很大,我想尽可能避免使用窗口函数。

我已自行加入库存表。但是,我很难找到消除不相关数据集的子句。

SELECT
    a.[date],
    a.Item,
    a.Location,
    a.qty,
    b.[date] LastChange,
    b.qty LastQty
FROM 
    #t a
LEFT JOIN 
    #t b ON a.Item = b.Item 
         AND a.location = b.location  
         AND b.date < a.date
WHERE   
    a.date = '2017-11-16'

【问题讨论】:

窗口函数比使用子查询更有效。寻找枢轴来执行此操作。 您使用的是哪个版本的 SQL Server?此外,还引入了窗口函数来提高此类情况的性能,那么为什么不使用窗口函数呢? 很公平,如果这有积极的影响,我会倾向于使用它们。服务器版本为 11.0.6598.0。谢谢:-) @JeanDoux :您的意思是在 MSSQL 中使用数据透视函数?这不是只对一列进行旋转吗? 【参考方案1】:

您需要一个额外的LEFT JOIN 以消除冗余记录:

SELECT a.[date], a.Item, a.Location, a.qty,
       b.[date] LastChange, b.qty LastQty
FROM t AS a
LEFT JOIN t AS b 
   ON a.Item = b.Item AND a.location = b.location AND b.date < a.date AND a.qty != b.qty
LEFT JOIN t AS c 
   ON c.Item = b.Item AND c.location = b.location AND c.date < b.date    
WHERE   
    a.[date] = '2017-11-16' AND c.Item IS NULL

使用

LEFT JOIN t AS c 
   ON c.Item = b.Item AND c.location = b.location AND c.date < b.date    

结合

WHERE   
    ... AND c.Item IS NULL

就像说:给我那些b没有其他的记录,c,记录更早的日期。

Demo here

使用FIRST_VALUE窗口函数:

;WITH CTE AS (
    SELECT [date], [Item], [Location], [Qty],
           FIRST_VALUE([date]) OVER (PARTITION BY [Item], [Location] 
                                     ORDER BY [date]) AS LastChange,
           FIRST_VALUE([Qty]) OVER (PARTITION BY [Item], [Location] 
                                    ORDER BY [date]) AS LastQty
    FROM t
)
SELECT [date], [Item], [Location], [Qty],
       IIF([Qty] != [LastQty], LastChange, NULL) AS LastChange,
       IIF([Qty] != [LastQty], LastQty, NULL) AS LastQty
FROM CTE
WHERE [date] = '2017-11-16' 

Demo here

【讨论】:

谢谢。在这里学到了很多。窗口函数确实必须更快!谢谢。 但是在使用 FIRST_VALUE 时存在一个问题。中间变化没有检测到。在您的演示中,您可以通过 C 文章看到这一点。最后一次更改应该是 15.11。不是 14.11。 @rasenkantenstein 那么您想在这种情况下检测 latest 记录吗?如果您有多个更改,例如('2017-11-16', 'C', 3, 2)、`('2017-11-16', 'C', 3, 8)('2017-11-16', 'C', 3, 5),该怎么办? 嗨,Giorgos,没错。上次更改的日期和更改的金额。这是一个每天创建一次的快照表,主键是日期+位置+项目。只有一种状态是可能的。但是窗口函数确实非常快。【参考方案2】:

试试这个查询

DECLARE @ReportDate date='20171116'

SELECT
  curData.[date],
  curData.Item,
  curData.Location,
  curData.Qty,
  lastData.[date] LastChange,
  lastData.Qty LastQty
FROM
  (
    SELECT *
    FROM #t
    WHERE [date]=@ReportDate
  ) curData
OUTER APPLY
  (
    SELECT TOP 1 *
    FROM #t lastData
    WHERE lastData.Item=curData.Item
      AND lastData.Location=curData.Location
      AND lastData.[date]<curData.[date]
      AND lastData.Qty<>curData.Qty
    ORDER BY lastData.[date] DESC
  ) lastData

【讨论】:

我改变了答案。在我的第一个版本中,我忘记使用条件lastData.Qty&lt;&gt;curData.Qty 嗨,到目前为止我还没有真正使用过 apply 这个概念。我可能想进一步探索这个。谢谢! @rasenkantenstein - SQL 是一种了不起的语言。祝你在这条路上取得成功!

以上是关于SQL Server:检测库存变化的主要内容,如果未能解决你的问题,请参考以下文章

SQL server:如何计算每个观察的最大连续变化

更新 WooCommerce 可变产品变化库存数量问题

sql server 触发器一个结果集变化更新到新表

为什么你SQL Server的数据库文件的Date modified没有变化呢?

使用SqlDependency实时监听SQL server数据库变化并执行事件

从创建索引过程中内存变化来看SQL Server与MySQL的内存淘汰算法