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

Posted

技术标签:

【中文标题】T-SQL:创建四个连接表的聚合视图的有效方法(每个项目的每个审计类型的最大审计条目)【英文标题】:T-SQL: Efficient way of creating an aggregate view of four joined tables (max audit-entry per audit type for each item) 【发布时间】:2012-05-06 15:21:53 【问题描述】:

对于一些 T-SQL 的帮助将不胜感激。

我有以下四张表:

项目(ID) ItemVersion(Id,FK 多对一 Item.Id) ItemVersionStatusLog(LogDate,FK 多对一 ItemVersion.Id,FK 多对一 StatusType.Id) StatusType(ID、别名)

我只列出了适合这个问题的列。

根实体是Item。对于每个Item,有一个或多个ItemVersion 条目。对于每个 ItemVersion 条目,有一个或多个 ItemVersionStatusLog 条目带有日期和对 StatusType 的引用(例如,创建、更新、禁用)。

我想通过在新表 (ItemStatus) 中创建一个聚合视图来总结每个 Item 的“最新状态”,我将在数据更改时回填并保持更新。对于每个 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

项目版本:

Id  | ItemId | Name
----------
1   | 1      | Apple
2   | 1      | Orange
3   | 1      | Plum
4   | 2      | Petrol
5   | 2      | Diesel
6   | 2      | LPG

状态类型:

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 项的预期结果是:

项目状态

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

【问题讨论】:

你试过什么?以及您拥有的数据和您期望的数据的样本会很棒。 这里贴一些示例数据集,不需要很多行,只要一个代表足以让我们更清楚地了解问题,并发布它的预期输出。借助有关问题的样本数据及其预期输出,它可以通过rubberducking 为回答者提供更多帮助 完成 - 希望它更清楚!干杯 给回答者一个提示,使用sqlfiddle.comText to DDL。复制 OP 的数据,然后将其粘贴到 Text to DDL 中,它会自动创建 DDL 和数据样本INSERT 语句。谢谢ツ 如果两个日期相等(和最大值)怎么办。我认为您的意思是结果第一行中的 VersionID 2。 【参考方案1】:
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

【讨论】:

谢谢,我在几个 mods 之后得到了这个结果(在 ItemId 和 StatusTypeId 上进行分区,因为我需要每种状态类型的最大日期结果) - 你能想到一个但是,在没有 CTE 的情况下这样做的方法,以便我可以在 Sql Compact 中运行相同的 T-SQL? @AlexNorcliffe - 我发布了另一个不使用 CTE 的版本。显然,CTE 会更快、更高效,但如果 CE 没有,那么您必须走更复杂的路线。 您的第 2 和第 3 个解决方案看起来很棒,但是我决定使用整数 ID 来简化问题让我很痛苦 :) 我在每个表中都使用 GUID id,所以不能做 Max on身份证。不过,我非常感谢您抽出宝贵时间,如果您没有时间进一步研究,鉴于我的问题详细信息中存在缺陷,我很乐意将您的问题标记为答案 @AlexNorcliffe - 关于整数 Id 很好,这只是意味着在两个条目具有相同状态和日期的情况下没有简单的平局。 嗯,这很奇怪,我确定第二个示例(没有 CTE)在那里有 Max(id) - 你编辑了吗?我会再试一次。谢谢!【参考方案2】:

如果您可以不使用 ItemVersion.Id,您可以使用 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

【讨论】:

谢谢,虽然我确实需要 ItemVersion.Id,因为聚合的目的是了解哪个版本具有最新的状态类型(对于每个项目)。因此,假设我需要知道每个项目的最新批准版本,但通过查询预先计算的聚合表而不是每次都进行大量子查询

以上是关于T-SQL:创建四个连接表的聚合视图的有效方法(每个项目的每个审计类型的最大审计条目)的主要内容,如果未能解决你的问题,请参考以下文章

使用自动布局约束以编程方式创建四个具有相同高度和宽度的 UIView

Js函数的三种创建四种调用

HTML & CSS:如何创建四个相同大小的标签,填充 100% 宽度?

是否有通过循环创建四次 1-12 块的最佳方法

T-SQL笔记

在 C++ 上使用 CImg 类显示多个图像