我有一个跟踪小部件位置的数据库(即跟踪单个项目的库存系统,而不是简单的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
更新后可能会显示多个Additive
和Subtractive
更新。
所以我的问题是:
“鉴于上次Complete
更新以及所有后续Complete
和Additive
次更新的设置,请获取当前的位置资源”
数据如下所示:
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
答案 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_VALUE
和OVER ... 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