Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

工作项在特定工作流状态或一系列状态中花费的时间是了解效率的一个重要方面。 “周期时间”和“提前期 分析”小组件提供一些状态时间度量值。 但是,这些小组件可能没有所需的详细信息级别。

本文提供了使用 DAX 计算来评估工作项在一系列状态中花费的时间的配方。 具体而言,你将了解如何将以下度量值和计算列添加到 Power BI 报表,并使用它们生成各种趋势图。 除列出的第一个字段外,所有字段都是计算列。

  • 分析不支持日内修订。 这些示例在引用 Analytics 视图时使用 “每日 ”间隔时精度最高。
  • 所有日内或周期内 (每周/每月) 修订将被计算忽略。 这可能会导致特定方案(例如,当工作项“正在进行”少于一天时显示没有时间“正在进行”的工作项)产生意外结果。
  • 尽可能使用 Power BI 默认聚合,而不是生成度量值。
  • 某些计算包括 +0 ,以确保每一行都包含数值,而不是 BLANK。 可能需要根据项目使用的工作流状态修改一些计算列定义。 例如,如果项目使用 New Active Closed 代替 Proposed In Progress Completed
  • 若要查看 Analytics 数据并查询服务,你需要是具有 基本 访问权限或更高访问权限的项目的成员。 默认情况下,向所有项目成员授予查询 Analytics 和定义 Analytics 视图 的权限。
  • 若要了解有关服务和功能启用以及常规数据跟踪活动的其他先决条件,请参阅 访问 Analytics 的权限和先决条件
  • 若要执行本文中所述的所有状态时间度量值,请确保在分析视图、Power Query或 OData 查询中包含以下字段:除默认字段外,“ 创建日期 ”和“ 状态类别 ”: “区域路径 ”、“ 分配到 ”、“ 迭代路径 ”、“ 状态 ”、“ 标题 ”、“ 工作项 ID” 和“ 工作项类型 ”。

    此外,请考虑使用基于 每日 粒度的分析视图。 本文中的示例基于基于自定义分析视图 在 Power BI 中创建活动 Bug 报表中定义的活动 Bug 分析视图 ,但选择了 60 天的 历史记录 每日 粒度除外。 此外,确定是要查看已完成的工作项还是已关闭的工作项。

    添加工作项计数度量值

    为了简化报表的快速生成,我们设计了用于 Power BI 中的默认聚合的分析视图。 为了说明默认聚合与度量值之间的差异,我们从简单的工作项计数度量值开始。

  • 将分析视图加载到Power BI Desktop。 有关详细信息,请参阅 使用 Power BI 数据连接器进行连接、连接到分析视图

  • 选择数据表,然后从功能区的“ 表工具 ”选项卡的“ 计算 ”部分,选择“ 新建度量值 ”。

  • 将默认文本替换为以下代码, 然后选择复选标记。

    Work Items Count=CALCULATE(COUNTROWS ('View Name'),LASTDATE ('View Name'[Date]))
    

    工作项计数度量值使用 CALCULATECOUNTROWS、 和 LASTDATE DAX 函数,本文稍后将更详细地介绍这些函数。

    请记得将 “视图名称” 替换为“分析”视图的表名。 例如,此处我们将 视图名称 替换为 活动 bug

    度量值与计算列有何不同

    度量值始终计算整个表,其中计算列特定于单个行。 有关详细信息,请参阅 DAX 中的计算列和度量值

    基于工作项 ID工作项计数度量值与默认计数聚合进行比较。 通过将 卡片 视觉对象和 工作项计数 度量值添加到第一张卡,将 工作项 ID 属性添加到第二张卡来创建下图。

    若要使用默认聚合获取正确的计数,请应用筛选器 Is Current 等于“True”。 将筛选器应用于默认聚合的这种模式是本文中提供的许多示例的基础。

    添加状态排序顺序

    默认情况下,Power BI 在可视化效果中按字母顺序显示状态。 如果要可视化状态中的时间,并且 “建议” 显示在 “正在进行”之后,可能会产生误导。 以下步骤有助于解决此问题。

  • 验证“ 状态类别” 字段是否包含在“分析”视图中。 此字段包含在所有默认共享视图中。

  • 选择数据表,然后从功能区的“ 表工具 ”选项卡的“ 计算 ”部分,选择“ 新建列”。

  • 将默认文本替换为以下代码, 然后选择复选标记。

    State Sort Order =  
    SWITCH (  
        'View Name'[State Category],  
        "Proposed", 1,  
        "InProgress", 2,  
        "Resolved", 3,  
    

    请参阅以下示例:

    如果需要的粒度高于 State Category 提供的粒度,则可能需要修改定义。 状态类别 在所有工作项类型之间提供正确的排序,而不考虑任何 状态 自定义。

  • 打开 “数据” 视图并选择“ 状态” 列。

  • 在“ 列工具 ”选项卡中,选择“ 按列排序 ”,然后选择“ 状态排序顺序 ”字段。

    若要解决这些问题,计算列应通过扫描 “日期 ”字段来查找前一天。

    若要添加 Date Previous 计算列,请从“ 表工具 ”选项卡中选择“ 新建列” ,然后将默认文本替换为以下代码并选择 复选标记。

    Date Previous =
    CALCULATE (
        MAX ( 'View Name'[Date] ),
            ALLEXCEPT ( 'View Name', 'View Name'[Work Item Id] ),
            'View Name'[Date] < EARLIER ( 'View Name'[Date] )
    

    Date Previous 计算列使用三个 DAX 函数、ALLEXCEPT、 和 EARLIER本文稍后会更详细地介绍这些函数MAX。 由于列是计算的,因此它将针对表中的每一行运行,每次运行时,它都有该特定行的上下文。

    从“ 日期 ”和“ 上一日期” 字段的上下文菜单中,选择“ 日期 (而不是 日期层次结构) ”以查看这些字段的单个日期。

    添加日期差异(以天为单位)

    Date Previous 计算每行上一个日期和当前日期之间的差值。 使用 日期差异(以天为单位),我们计算每个时间段之间的天数计数。 对于每日快照中的大多数行,该值等于 1。 但是,对于数据集中存在间隙的许多工作项,该值大于 1

    要求已将 “上一个日期 ”计算列添加到表中。

    请务必考虑数据集的第一天,其中 “上一个日期” 为空。 在此示例中,我们为该行提供标准值 1 以保持计算一致。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码并选择 复选标记。

    Date Diff in Days =
        ISBLANK ( 'View Name'[Date Previous] ),
        DATEDIFF (
            'View Name'[Date Previous],
            'View Name'[Date],
    

    此计算列使用ISBLANK本文稍后介绍的DATEDIFF DAX 函数。

    添加为状态的最后一天

    在下一步中,我们计算给定行是否表示特定工作项处于状态的最后一天。 它支持 Power BI 中的默认聚合,我们将在下一部分中添加 “状态时间(天 )”列。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码并选择 复选标记。

    Is Last Day in State = 
    ISBLANK (CALCULATE (
        COUNTROWS ( 'View Name' ),
            ALLEXCEPT ( 'View Name', 'View Name'[Work Item Id] ),
            'View Name'[Date] > EARLIER ( 'View Name'[Date] ),
            'View Name'[State] = EARLIER ( 'View Name'[State] )
    

    添加状态时间(以天为单位)

    现在,可以通过对每个工作项的 日期差异(以天为单位 )求和来计算工作项在特定状态下花费的时间。 此计算包括在特定状态下花费的所有时间,即使它已多次在状态之间切换。 可以使用 “日期 ”或“状态的最后一天”将每一行评估为趋势,或者使用“状态 为最后一天”的最新信息。

    要求已将 Date Diff in DaysIs Last Day in State 计算列添加到表中。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码并选择 复选标记。

    State Time in Days = 
    CALCULATE (
        SUM ( 'View Name'[Date Diff in Days] ),
        ALLEXCEPT ( 'View Name', 'View Name'[Work Item Id] ),
        'View Name'[Date] <= EARLIER ( 'View Name'[Date] ),
        'View Name'[State] = EARLIER ( 'View Name'[State] )
    ) + 0
    

    创建基于状态时间(以天为单位)的堆积柱形图

    为了演示“ 状态时间(以天为单位 )”列,将创建以下堆积柱形图。 第一个图表显示一段时间内每个状态的工作项计数。

    第二个图表演示了活动工作项处于特定状态的平均天数趋势。

    添加状态时间(以天为单位 ) - 最新 (是状态) 的最后一天

    在计算表中每个工作项的处于状态的时间或按字段(如 “区域路径”)进行筛选时,请勿在聚合中使用 “状态时间(以天为单位 )”列。 聚合使用工作项处于 状态的每一天的值。 例如,如果工作项在星期一处于 “正在进行” 状态,并在星期四移动到 “已完成 ”,则状态时间为 3 天,但“ 状态时间(以天为单位 )”列的总和为 6 天, (1+2+3) 这显然不正确。

    若要解决此问题,请使用 “状态时间”(以天为单位 ),并应用筛选器 “状态的最后一天 ”等于“True”。 它消除了趋势所需的所有历史数据,而是只关注每个状态的最新值。

    添加状态时间(以天为单位 - 正在进行中)

    在上面的示例中,仅当工作项处于该特定状态时,才会计算给定工作项的状态时间( 以天 为单位)。 如果目标是使给定工作项的状态时间连续计入平均值,则必须更改计算。 例如,如果要跟踪“正在进行”状态,请添加 “状态时间(以天为单位 ) - 正在进行” 计算列。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码并选择 复选标记。

    State Time in Days - In Progress = 
    CALCULATE (
        SUM ( 'View Name'[Date Diff in Days] ),
        ALLEXCEPT ( 'View Name', 'View Name'[Work Item Id] ),
       'View Name'[Date] <= EARLIER('View Name'[Date]),
       'View Name'[State] = "In Progress"
    ) + 0
    

    可能需要根据项目使用的工作流状态修改定义。 例如,本文示例中使用的项目使用“正在进行”工作流状态,但敏捷、Scrum 和 CMMI 进程通常使用“活动”或“已提交”状态来表示正在进行的工作。 有关概述,请参阅 工作流状态和状态类别

    下图显示了考虑每个现有工作项的所有状态时间的效果, (左) 显示,而只考虑给定日期处于特定状态的工作项, (右) 显示。

    多个状态的状态时间(以天为单位)趋势

    还可以使用“连续”模式跨多个状态分析性能。 但是,此方法仅适用于趋势图。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码并选择 复选标记。

    State Time in Days - Working States = 
    CALCULATE (
        SUM ( 'View Name'[Date Diff in Days] ),
        ALLEXCEPT ( 'View Name', 'View Name'[Work Item Id] ),
       'View Name'[Date] <= EARLIER('View Name'[Date]),
       'View Name'[State] IN { "Committed", "In Progress" }
    ) + 0
    

    可能需要根据项目使用的工作流状态修改定义。 例如,如果项目使用“Active”代替“Committed”或“Proposed”。

    左侧的图表显示组合平均值,右侧显示每个单独的状态。

    获取多个状态的状态时间(以天为单位- 最新)

    创建趋势时,请使用 “状态时间”(天 - 最新 计算)列。 使用状态筛选器,“ 状态时间(天) ”列和“ 状态的最后一天” 提供了一种简单方法来获取任何工作项或工作项组在一组状态中花费的总时间。

    添加上一个状态

    日期上一个 ”计算列还可用于查找过去值,例如每个工作项的上一个状态。

    要求已将 “上一个日期 ”计算列 添加到表中。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码并选择 复选标记。

    State Previous =
    LOOKUPVALUE (
        'View Name'[State],
        'View Name'[Work Item Id], 'View Name'[Work Item Id],
        'View Name'[Date], 'View Name'[Date Previous]
    

    此计算列使用 LOOKUPVALUE本文稍后介绍的

    第一个 LOOKUPVALUE 参数 'View Name'[State]指定返回 [State] 的值。

    下一个参数 'View Name'[Work Item Id], 'View Name'[Work Item Id]指定仅考虑具有匹配工作项 ID 的行作为当前行。

    最后一个参数 'View Name'[Date], 'View Name'[Date Previous]指定返回的行的日期必须具有与当前行的 [上一个日期] 匹配的 [Date]。 在快照中,只有一行符合此条件。

    添加状态已更改

    使用 “状态上一列 ”,我们可以标记发生状态转换的每个工作项的行。 阶段更改计算列有两个特殊注意事项:

  • 上一个状态 ”的空白值,我们设置为工作项的 “创建日期
  • 创建工作项被视为状态转换
  • 要求已将 “上一个状态” 计算列添加到表中。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码并选择 复选标记。

    State Changed =
        ISBLANK ( 'View Name'[State Previous] ),
        'View Name'[Created Date].[Date] = 'View Name'[Date],
        'View Name'[State Previous] <> 'View Name'[State]
    

    计算列是一个布尔值,用于标识行是否为状态转换。 通过使用 Not Equal To 运算符,可以正确捕获先前状态与当前状态不匹配的行,这意味着比较将按预期返回 True。

    添加状态流

    使用 State PreviousState Changed 计算列,可以创建一个列来说明给定工作项 的状态流 。 对于本文而言,创建此列是可选的。

    要求已将 “上一个状态”“状态已更改” 计算列添加到表中。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码并选择 复选标记。

    State Flow = 
    IF([State Changed], [State Previous], [State]) & " => " & [State]
    

    添加状态更改计数

    当我们进入更复杂的度量值时,我们需要具有状态更改总数的表示形式,以比较给定工作项的数据行。 我们通过添加 状态更改计数 计算列来获取表示形式。

    要求已将 State Changed 计算列添加到表中。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码,然后选择 复选标记。

    State Change Count = 
    CALCULATE (
        COUNTROWS ( 'View Name' ),
        ALLEXCEPT ( 'View Name', 'View Name'[Work Item Id] ),
        'View Name'[Date] <= EARLIER ( 'View Name'[Date] ),
        'View Name'[State Changed]
    ) + 0
    

    添加状态更改计数 - 上次建议和状态重启时间(以天为单位)

    计算重启 状态(以天为单位)是一个相当复杂的计算。 第一步是查找工作项最后一次处于建议状态的时间。 添加 “状态更改计数 - 上次建议的 计算”列。

    可能需要根据项目使用的工作流状态修改以下定义。 例如,如果项目使用“New”代替“Proposed”。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码,然后选择 复选标记。

    State Change Count - Last Proposed = 
    CALCULATE (
        MAX ( 'View Name'[State Change Count] ),
        ALLEXCEPT ( 'View Name', 'View Name'[Work Item Id] ),
        'View Name'[Date] <= EARLIER ( 'View Name'[Date] ),
        'View Name'[State] = "Proposed"
    

    然后,进一步回顾过去,看看在此建议状态之前是否有一些活动状态。 最后,总结工作项在上次建议之前处于活动状态的所有天数。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码,然后选择 复选标记。

    State Restart Time in Days = 
    CALCULATE (
        SUM ( 'View Name'[Date Diff in Days] ),
        ALLEXCEPT ( 'View Name', 'View Name'[Work Item Id] ),
        'View Name'[Date] <= EARLIER ( 'View Name'[Date] ),
        'View Name'[State Change Count] < EARLIER('View Name'[State Change Count - Last Proposed] ),
        'View Name'[State] <"Proposed"
    ) + 0
    

    由于每行数据 的状态重启时间(以天为单位 )已更新,因此可以创建一个趋势来评估跨特定冲刺的返工,或使用“ 是当前”检查单个工作项的返工。

    添加状态返工时间(以天为单位)

    状态重启时间(以天为单位)类似, 状态返工时间(以天为单位 )查找工作项首次处于 “已完成 ”状态类别的时间。 在该时间之后,每天工作项花费在“已完成”以外的状态时,都计为返工。

    在“ 建模 ”选项卡中,选择“ 新建列 ”,然后将默认文本替换为以下代码,然后选择 复选标记。

    State Rework Time in Days = 
        ISBLANK ( 'View Name'[State Change Count - First Completed] ),
        CALCULATE (
            SUM ( 'View Name'[Date Diff in Days] ),
            ALLEXCEPT ( 'View Name', 'View Name'[Work Item Id] ),
            'View Name'[Date] <= EARLIER ( 'View Name'[Date] ),
            'View Name'[State Change Count] EARLIER ( 'View Name'[State Change Count - First Completed] ),
            'View Name'[State] IN {"Completed", "Closed", "Cut" } = FALSE()
        ) + 0
    

    可能需要根据项目使用的工作流状态修改上述定义。 例如,如果项目使用 “完成” 代替 Closed

    DAX 函数

    本节中提供了用于创建本文中添加的计算列和度量值的 DAX 函数的其他信息。 另请参阅 DAX、时间智能函数

    ALLEXCEPT 删除表中所有上下文筛选器,已应用于指定列的筛选器除外。 实质上, ALLEXCEPT ('View Name'', 'View Name'[Work Item Id]) 会将表中的行减少到仅与当前行共享相同工作项 ID 的行。 CALCULATE 此函数是几乎所有示例的基础。 基本结构是一个表达式,后跟应用于表达式的一系列筛选器。 COUNTROWS 此函数 COUNTROWS ( 'View Name' )只是计算应用筛选器后剩余的行数。 DATEDIFF 返回两个日期之间跨越的间隔边界的计数。 DATEDIFF“日期 ”中减去 “上一个日期 ”,以确定它们之间的天数。 EARLIER 返回所述列的外部计算传递中指定列的当前值。 例如, 'View Name'[Date] < EARLIER ( 'View Name'[Date] )进一步将数据集减少到仅发生在使用 EARLIER 函数引用的当前行的日期之前的那些行。 EARLIER 不引用以前的日期,它专门定义计算列的行上下文。 ISBLANK 检查值是否为空白,并返回 TRUE 或 FALSE。 ISBLANK 计算当前行以确定 “上 一个日期”是否具有值。 否则,If 语句将 Date Diff in Days 设置为 1。 LASTDATE 我们将筛选器应用于 LASTDATE 表达式(例如 LASTDATE ( 'View Name'[Date] )),以查找表中所有行的最新日期,并消除不共享同一日期的行。 对于分析视图生成的快照表,此筛选器可以有效地选取所选时间段的最后一天。 LOOKUPVALUE 在“result_columnName”中,为满足“search_columnName”和“search_value”指定的所有条件的行返回值。 返回列中或两个标量表达式之间的最大数字值。 我们应用 MAX ( 'View Name'[Date] ),以确定应用所有筛选器后最近的日期。
  • Power BI 集成概述
  • 创建分析视图
  • Power BI Desktop 入门
  • Power BI 连接器的数据集设计
  • 工作流状态和状态类别
  • Analytics 数据模型
  •