对某些T-SQL的一些帮助将非常受欢迎。
我有以下四个表:
我只列出了适合此问题的列。
根实体是Item
。对于每个Item
,都有一个或多个ItemVersion
条目。对于每个ItemVersion
条目,有一个或多个ItemVersionStatusLog
条目,其中包含日期和对StatusType
的引用(例如,已创建,更新,已禁用)。
我想通过在新表(Item
)中创建聚合视图来总结每个ItemStatus
的“最新状态”,我将回填这些视图,然后在数据更改时保持更新。对于每个Item和StatusType
对,聚合应该在日志表中给出最大日期条目。因此,我有一个快照,对于每个StatusType
,我可以获得项目的最新ItemVersion
。
另一种表达方式是程序性的:
For each Item
- For each StatusType
- - List the ItemVersion Id with the maximum date from ItemVersionStatusLog given the correct StatusType
聚合视图或表的目标列是:
Item Id, ItemVersion Id, Date (from ItemVersionStatus), StatusType Id
虽然使用UDF可以很好地完成这项工作,但如果可能的话,我很乐意在单个SQL语句中执行此操作。我的主要目标是SQL Server 2008,但SQL Server Compact 4也没有太多修改,所以依赖UDF不是一个很好的选择,但任何帮助都表示赞赏:)
更新 - 一些示例数据
Item:
Id
--
1
2
ItemVersion:
Id | ItemId | Name
----------
1 | 1 | Apple
2 | 1 | Orange
3 | 1 | Plum
4 | 2 | Petrol
5 | 2 | Diesel
6 | 2 | LPG
StatusType:
Id | Alias
-----------
1 | Created
2 | Approved
3 | Published
4 | Deleted
ItemVersionStatusLog:
Id | ItemVersionId | StatusTypeId | Date
------------------------------------------
1 | 1 | 1 | 2012-01-01 00:00
2 | 1 | 4 | 2012-01-01 00:05
3 | 2 | 1 | 2012-01-01 00:10
4 | 2 | 3 | 2012-01-01 00:15
5 | 3 | 1 | 2012-01-01 00:20
6 | 3 | 3 | 2012-01-01 00:25
在这种情况下,第1项的预期结果为:
ItemStatus
ItemId | ItemVersionId | Date | StatusTypeId
--------------------------------------------------------
1 | 3 | 2012-01-01 00:20 | 1
1 | 3 | 2012-01-01 00:25 | 3
1 | 1 | 2012-01-01 00:05 | 4
答案 0 :(得分:4)
With MostRecentStatus As
(
Select ItemVersionId, StatusTypeId, [Date]
, Row_Number() Over ( Partition By StatusTypeId Order By [Date] Desc ) As Rnk
From ItemVersionStatusLog As IVSL
)
Select IV.ItemId, M.ItemVersionId, M.[Date], M.StatusTypeId
From MostRecentStatus As M
Join ItemVersion As IV
On IV.Id = M.ItemVersionId
Where Rnk = 1
不使用CTE的版本:
Select IV.ItemId, IVSL.ItemVersionId, IVSL.[Date], IVSL.StatusTypeId
From ItemVersionStatusLog As IVSL
Join (
Select IVSL2.StatusTypeId, Max([Date]) As [Date]
From ItemVersionStatusLog As IVSL2
Group By IVSL2.StatusTypeId
) As Z
On Z.StatusTypeId = IVSL.StatusTypeId
And Z.[Date] = IVSL.[Date]
Join ItemVersion As IV
On IV.Id = IVSL.ItemVersionId
上述解决方案的一个问题是它不允许在相同日期和时间对同一状态进行多次输入。如果我们可以假设在这种情况下,使用最后ItemVersionStatusLog.Id
值,那么我们会这样调整:
Select IV.ItemId, IVSL.ItemVersionId, IVSL.[Date], IVSL.StatusTypeId
From ItemVersionStatusLog As IVSL
Join (
Select IVSL1.StatusTypeId, IVSL1.[Date], Max(IVSL1.Id) As Id
From ItemVersionStatusLog As IVSL1
Join (
Select IVSL2.StatusTypeId, Max([Date]) As [Date]
From ItemVersionStatusLog As IVSL2
Group By IVSL2.StatusTypeId
) As Z
On Z.StatusTypeId = IVSL1.StatusTypeId
And Z.[Date] = IVSL1.[Date]
Group By IVSL1.StatusTypeId, IVSL1.[Date]
) As MostRecentStatus
On MostRecentStatus.Id = IVSL.Id
Join ItemVersion As IV
On IV.Id = IVSL.ItemVersionId
答案 1 :(得分:1)
注意:我添加了第三个解决方案。
1)对于SQL Server 2005+
托马斯的解决方案(+1 / ROW_NUMBER()
)应该可以胜任。
2)对于SQL Server CE
,您可以尝试这三种解决方案:
2.1)
SELECT iv.ItemId,
ivsl.ItemVersionId,
ivsl.StatusTypeId,
ivsl.[Date]
FROM ItemVersion iv
INNER JOIN ItemVersionStatusLog ivsl ON iv.Id = ivsl.ItemVersionId
AND ivsl.[Date] >= ALL
(
SELECT ivsl2.[Date]
FROM ItemVersion iv2
INNER JOIN ItemVersionStatusLog ivsl2 ON iv2.Id = ivsl2.ItemVersionId
WHERE iv2.ItemId = iv.ItemId
AND ivsl2.StatusTypeId = ivsl.StatusTypeId
);
2.2)
SELECT iv.ItemId,
ivsl.ItemVersionId,
ivsl.StatusTypeId,
ivsl.[Date]
FROM ItemVersion iv
INNER JOIN ItemVersionStatusLog ivsl ON iv.Id = ivsl.ItemVersionId
INNER JOIN
(
SELECT iv2.ItemId,
ivsl2.StatusTypeId,
MAX(ivsl2.[Date]) AS MaxDate
FROM ItemVersion iv2
INNER JOIN ItemVersionStatusLog ivsl2 ON iv2.Id = ivsl2.ItemVersionId
GROUP BY iv2.ItemId, ivsl2.StatusTypeId
) x ON iv.ItemId = x.ItemId AND ivsl.StatusTypeId = x.StatusTypeId AND ivsl.[Date] = x.MaxDate
结果(两种解决方案):
ItemId ItemVersionId StatusTypeId Date
----------- ------------- ------------ -----------------------
1 1 4 2012-01-01 00:05:00.000
1 3 1 2012-01-01 00:20:00.000
1 3 3 2012-01-01 00:25:00.000
注意:如果您有(ItemId, StatusTypeId, Date)
个重复项,这两个解决方案都无效。
2.3)即使您有(ItemId, StatusTypeId, Date)
个重复项,下一个解决方案也应该有用,对于UNIQUEIDENTIFIER
个ID,您需要与BINARY(16)
进行转换(对于LastItemVersionId
)。
SELECT iv.ItemId,
--CONVERT(UNIQUEIDENTIFIER, MAX(CONVERT(BINARY(16),ivsl.ItemVersionId))) AS LastItemVersionId,
MAX(ivsl.ItemVersionId) AS LastItemVersionId,
ivsl.StatusTypeId,
ivsl.[Date]
FROM ItemVersion iv
INNER JOIN ItemVersionStatusLog ivsl ON iv.Id = ivsl.ItemVersionId
INNER JOIN
(
SELECT iv2.ItemId,
ivsl2.StatusTypeId,
MAX(ivsl2.[Date]) AS MaxDate
FROM ItemVersion iv2
INNER JOIN ItemVersionStatusLog ivsl2 ON iv2.Id = ivsl2.ItemVersionId
GROUP BY iv2.ItemId, ivsl2.StatusTypeId
) x ON iv.ItemId = x.ItemId AND ivsl.StatusTypeId = x.StatusTypeId AND ivsl.[Date] = x.MaxDate
GROUP BY iv.ItemId, ivsl.StatusTypeId, ivsl.[Date]
答案 2 :(得分:0)
如果你没有ItemVersion就可以生活。你可以使用GROUP BY:
SELECT
item.Id,
MAX(log.LogDate) as LogDate,
status.Id
FROM Item item
JOIN ItemVersion version ON item.Id = version.ItemId
JOIN ItemVersionStatusLog log ON version.Id = log.ItemVersionId
JOIN StatusType status ON log.StatusTypeId = status.Id
GROUP BY
item.Id,
status.Id