考虑DateTime除外?

时间:2014-03-07 08:16:27

标签: sql sql-server set boolean-logic inventory-management

我有一个跟踪小部件位置的数据库(即跟踪单个项目的库存系统,而不是简单的SKU计数)。

有两个表:

 InventoryUpdates (
     UpdateId   bigint PRIMARY KEY,
     LocationId bigint,
     Type       tinyint, -- an enum column
     DateTime   datetimoffset
 )

 WidgetState {
     SerialNumber varchar(20) PRIMARY KEY,
     UpdateId     bigint
 }

说明:

广告资源更新是“完整”,“添加”或“减法”(这由Type列决定)。当更新为Complete时,表示关联的WidgetState集合代表整个广告资源(即,如果列出了小部件,那么它就在那里;缺少小部件是肯定的,它不存在在那个时候的库存)。当更新为Additive时,这意味着需要通过总结自上次Additive更新以来的所有后续Complete更新来确定库存(就像增量备份一样),最后是{ {1}}更新意味着应从广告资源中排除更新中包含的项目。当然,Subtractive更新后可能会显示多个AdditiveSubtractive更新。

所以我的问题是:

“鉴于上次Complete更新以及所有后续CompleteAdditive次更新的设置,请获取当前的位置资源”

数据如下所示:

Subtractive

在这个简单的示例中,我们看到已知唯一窗口小部件InventoryUpdates ---------------------------------------------- UpdateId LocationId Type DateTime 1 1 Complete 2014-01-01 2 1 Additive 2014-01-02 3 1 Subtractive 2014-01-03 4 1 Additive 2014-01-04 WidgetState ---------------------------------------------- SerialNumber UpdateId 0001 1 0004 1 008B 2 0001 3 0004 3 0004 4 位于更新1中的清单中,然后在更新3中将其删除,然后在更新4中重新添加。

假设我想找出当前的库存,我的结果集应该是:

0004

可能有一个非常简单的解决方案,我现在无法想到它。我想它可能看起来像这样:

WidgetsInInventory
----------------------------------------------
SerialNumber
0004
008B

...但是当一个减法发生时,无论事件发生在早期的DateTime值而不是后续的Addition中,它都会崩溃。

更新

我在回家的路上只想到这可能是一个有效的解决方案,明天我会测试它。

下面的代码使用SELECT DISTINCT SerialNumber, Type FROM WidgetState INNER JOIN InventoryUpdates ON WidgetState.UpdateId = InventoryUpdates.UpdateId WHERE InventoryUpdates.Type = Complete OR InventoryUpdates.Type = Additive ORDER BY InventoryUpdates.DateTime DESC EXCEPT SELECT DISTINCT SerialNumber, Type FROM WidgetState INNER JOIN InventoryUpdates ON WidgetState.UpdateId = InventoryUpdates.UpdateId WHERE InventoryUpdates.Type = Subtractive ORDER BY InventoryUpdates.DateTime DESC DISTINCT相结合来获取每个ORDER BY最后一次出现,因此如果最后一次出现是WidgetState然后它会取消任何先前的Addition,反之亦然(好吧,如果最后一次出现是Subtraction那么它可以很容易地从未来的超级查询中排除{{1谓词)。它刚刚03:00h消失,我没有机会尝试它,但理论听起来是否正确?谢谢!

Subtraction

2 个答案:

答案 0 :(得分:1)

我会采用不同的方法。只需计算小部件。如果更新为Subtractive,则输入-1;对于Complete和Additive,输入1。然后简单地对标志求和并过滤那些非零(正)和。

declare @dateTimeOfLastCompleteUpdate Date = '20140101'
declare @locationId int  = 1
;
with cte1 as
(
  select
    ws.SerialNumber,
    ws.UpdateId,
    CASE WHEN iu.Type = 'Subtractive' THEN -1 ELSE 1 END as Flag
  from 
    InventoryUpdates iu
    inner join WidgetState ws
      on ws.UpdateId = iu.UpdateId
  where
    iu.[DateTime] >= @dateTimeOfLastCompleteUpdate
    and iu.LocationId = @locationId
)
, cte2 as
(
  select
    SerialNumber,
    Flag,
    ROW_NUMBER() OVER(partition by SerialNumber order by UpdateId) as rn
  from
    cte1
)
, cte3 as
(
  select
    SerialNumber,
    sum(flag) as WidgetCount
  from 
    cte2 a
  where
    not exists 
    (
      select * 
      from cte2 b 
      where b.serialnumber = a.serialnumber
      and b.rn = a.rn - 1
      and b.flag = a.flag
    )
  group by
    SerialNumber
)
select SerialNumber from cte3 where WidgetCount = 1

这是fiddle

答案 1 :(得分:0)

我做了一些调查,发现LAST_VALUEOVER ... PARTITION BY一起使用,但LAST_VALUE仅在SQL Server 2012上支持。我使用2008 R2,所以我需要找到相同的东西。

幸运的是:您可以将ROW_NUMBER添加到PARTITION,然后在正确排序时选择最高值(WHERE rowNumber = 1),这是我的最终查询:

SELECT
    *
FROM
    (
    SELECT
        WidgetStates.TId,
        InventoryUpdates.*,
        ROW_NUMBER() OVER (PARTITION BY WidgetStates.Tid ORDER BY InventoryUpdates.DateTime DESC) AS RowN
    FROM
        WidgetStates
        INNER JOIN InventoryUpdates ON WidgetStates.UpdateId = InventoryUpdates.UpdateId
    WHERE
        InventoryUpdates.DateTime >= @lastCompleteUpdateDateTime
        AND
        InventoryUpdates.LocationId = @locationId
    ) As Results
WHERE
    Results.RowN = 1
    AND
    Results.Type >= 0