T-SQL:创建四个连接表的聚合视图的有效方法(每个项目的每个审计类型的最大审计条目)

时间:2012-05-06 15:21:54

标签: sql performance sql-server-2008 tsql sql-server-ce

对某些T-SQL的一些帮助将非常受欢迎。

我有以下四个表:

  • 项目(Id)
  • ItemVersion(Id,FK多对一Item.Id)
  • ItemVersionStatusLog(LogDate,FK多对一ItemVersion.Id,FK多对一StatusType.Id)
  • StatusType(Id,Alias)

我只列出了适合此问题的列。

根实体是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

3 个答案:

答案 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

SQL Fiddle Version

不使用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

SQL Fiddle Version

上述解决方案的一个问题是它不允许在相同日期和时间对同一状态进行多次输入。如果我们可以假设在这种情况下,使用最后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

SQL Fiddle Version

答案 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