Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
扩展开发人员可以遵循本文中提供的针对 Azure DevOps 的 Analytics 设计高效 OData 查询的准则,从而受益匪浅。 遵循这些准则将有助于确保查询在执行时间和资源消耗时具有良好的性能。 不符合这些准则的查询可能会导致性能不佳,报告等待时间过长、查询超过允许的资源消耗或服务阻塞。
将自动为所有Azure DevOps Services启用 Analytics 服务。 支持在生产环境中使用它。
Power BI 集成
和对分析服务的
OData 源
的访问权限已正式发布。 我们鼓励你使用它并向我们提供反馈。 >
可用数据取决于版本。 支持的最新版本为
v2.0
,最新预览版本为
v4.0-preview
。 若要了解详细信息,请参阅
OData API 版本控制
。
Analytics 服务会自动安装在Azure DevOps Server 2020 及更高版本的所有新项目集合上。 支持在生产环境中使用它。
Power BI 集成
和对分析服务的
OData 源
的访问权限已正式发布。 我们鼓励你使用它并向我们提供反馈。 如果从 Azure DevOps Server 2019 升级,则会提供在升级期间安装 Analytics 服务的选项。
可用数据取决于版本。 支持的最新版本为
v2.0
,最新预览版本为
v4.0-preview
。 若要了解详细信息,请参阅
OData API 版本控制
。
Analytics 服务在 2019 Azure DevOps Server处于预览状态。 可以通过为项目集合
启用或安装 Analytics 来访问它
。
Power BI 集成
和对分析服务的
OData 源
的访问处于预览状态。 我们鼓励你使用它并向我们提供反馈。
可用数据取决于版本。 支持的最新版本为
v2.0
,最新预览版本为
v4.0-preview
。 若要了解详细信息,请参阅
OData API 版本控制
。
准则组织为简单建议,前缀为
“DO
”、“
考虑
”、“
避免
”和
“不要
”。 Analytics 强制实施的限制性规则包含
[BLOCKED]
前缀。 通过这些指南,你应该了解不同解决方案之间的权衡。 在某些情况下,你可能有数据要求,迫使你违反一个或多个准则。 这种情况应该很少见。 建议有明确且令人信服的理由做出此类决定。
本文档中显示的示例基于Azure DevOps Services URL,需要在Azure DevOps Server URL 中替换 。
https://{servername}:{port}/tfs/{OrganizationName}/{ProjectName}/_odata/{version}/
错误和警告消息
✔️ 查看 OData 响应警告
执行的每个查询都会根据一组预定义规则进行检查。 冲突在 之后 @vsts.warnings
的 OData 响应中返回。 查看这些警告,因为它们提供了有关如何改进查询的当前和上下文相关的信息。
"@odata.context": "https://{OrganizationName}.tfsallin.net/_odata/v1.0/$metadata#WorkItems",
"@vsts.warnings": [
"The specified query does not include a $select or $apply clause which is recommended for all queries."
✔️ 查看 OData 错误消息
违反 OData 错误规则的查询将导致失败响应,并显示 400 (错误请求) 状态代码。 关联消息不会显示在 属性中 @vsts.warnings
。 相反,他们将在 JSON 响应的 message
属性中生成错误消息。
"error": {
"code": "0",
"message": "The query specified in the URI is not valid. The Snapshot tables in Analytics are intended to be used only in an aggregation."
✔️ 将查询限制为你有权访问的项目 ()
✔️ 如果扩展可能包含其他可能不可访问的项目中的数据,请在 子句中 $expand
指定项目筛选器
✔️ 如果查询超出使用限制,请等待或停止操作
✔️ 如果查询因超时而失败,请等待或停止操作
✔️ 在快照表上聚合时,请务必在 DateSK
子句中包含 groupby
或 DateValue
列
✔️ 使用筛选器子句显式寻址实体
✔️ WorkItemRevisions
使用实体集加载给定工作项的所有修订
✔️ 对长查询使用批处理终结点
✔️ 对日期列进行筛选时,请指定时区
✔️ 考虑使用项目范围的终结点进行查询
❌ [已阻止]请勿将快照实体用于聚合以外的任何内容
❌ [已阻止]请勿在资源路径中使用实体键进行实体寻址
❌[已阻止]不要在实体上WorkItem
展开Revisions
❌ [已阻止]不要对不同列进行分组
❌ [已阻止]不使用 countdistinct
聚合
❌ [已阻止]不要使用批处理终结点发送多个查询
❌ [已阻止]不要使用导致超过 800 列的查询
❌ 避免可能导致算术溢出的聚合
❌ 避免创建长查询
✔️ 将查询限制为你有权访问的项目 ()
如果查询以您无权访问的项目中的数据为目标,则查询将返回“项目访问被拒绝”消息。 若要确保你具有访问权限,请确保对查询的所有项目将 “查看分析 ”权限设置为“允许”。 若要了解详细信息,请参阅 访问 Analytics 所需的权限。
如果你无权访问项目,你将看到以下消息:
查询结果包括你无权访问的一个或多个项目中的数据。 添加一个或多个项目筛选器,以指定在“WorkItems”实体中有权访问的项目 () 。 如果使用$expand或导航属性,则这些实体需要项目筛选器。
若要解决此问题,可以显式添加项目筛选器,或使用项目 范围的终结点 ,如本文稍后所述。
例如,以下查询提取属于名为 {projectSK1}
和 {projectSK2}
的项目的工作项。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=ProjectSK eq {projectSK1} or ProjectSK eq {projectSK2}
&$select=WorkItemId, Title
✔️ 如果扩展可能包含其他可能不可访问的项目中的数据,请在 子句中 $expand
指定项目筛选器
展开导航属性时,有可能最终引用其他无法访问的项目中的数据。 如果引用不可访问的数据,将收到前面列出的相同错误消息,“查询结果包含一个或多个项目中的数据...”。 同样,可以通过添加显式项目筛选器来控制扩展的数据来解决此问题。
可以在简单导航属性的正则 $filter
子句中执行此操作。 例如,下面的查询显式询问 WorkItemLinks
链接及其目标在同一项目中的位置。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemLinks?
$filter=ProjectSK eq {projectSK} and TargetWorkItem/ProjectSK eq {projectSK}
&$select=LinkTypeReferenceName, SourceWorkItemId, TargetWorkItemId
&$expand=TargetWorkItem($select=WorkItemId, Title)
相反,可以在 子句中$expand
移动筛选器以$filter
展开选项。 但是,它会更改查询的语义。 例如,以下查询获取给定项目中的所有链接,并且仅当目标存在于同一项目中时才有条件地展开该目标。 虽然此方法有效,但可能会导致混淆,因为可能很难确定某个属性是否未扩展,因为该 null
属性是因为它被筛选出来,还是因为它已被筛选掉。仅当确实需要此特定行为时,才使用此解决方案。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemLinks?
$filter=ProjectSK eq {projectSK}
&$select=LinkTypeReferenceName, SourceWorkItemId, TargetWorkItemId
&$expand=TargetWorkItem($filter=ProjectSK eq {projectSK}; $select=WorkItemId, Title)
你会发现, $filter
当使用 expand 集合属性(例如 Children
在 WorkItems
实体集中)时,expand 选项非常有用。 例如,以下查询返回给定项目中的所有工作项及其属于同一项目的所有子项。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=ProjectSK eq {projectSK}
&$select=WorkItemId, Title
&$expand=Children($filter=ProjectSK eq {projectSK}; $select=WorkItemId, Title)
如果展开以下属性之一,则需要指定筛选器:
WorkItems
实体集: Parent
、 Children
WorkItemLinks
实体集: TargetWorkItem
。
✔️ 考虑使用项目范围的终结点进行查询
如果对单个项目中的数据感兴趣,建议使用项目范围的 OData 终结点 (/{ProjectName}/_odata/v1.0
) 。 它避免了前面两节中描述的问题,并将数据隐式筛选到一个项目、引用的实体集和所有展开的导航属性。
通过这种简化,可以将上一部分中的查询重写为以下形式。 不仅 expand 子句中的筛选器消失,而且不需要主实体集上的筛选器。
https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}//WorkItemLinks?
&$select=LinkTypeReferenceName, SourceWorkItemId, TargetWorkItemId
&$expand=TargetWorkItem($select=WorkItemId, Title)
对工作项子项的查询也更短、更简单。
https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}//WorkItems?
&$select=WorkItemId, Title
&$expand=Children($select=WorkItemId, Title)
仅当焦点是单个项目中的数据时,才能应用此解决方案。 对于跨项目报告,必须使用前面部分所述的筛选策略。
✔️ 如果查询超出使用限制,请等待或停止操作
如果执行许多查询,或者查询需要许多资源才能运行,则可能会超出服务限制并暂时被阻止。 如果超出服务限制,请停止操作,因为发送的下一个查询可能失败并显示相同的错误消息。
由于超出命名空间“{namespace}”中的资源“{resource}”使用量,请求被阻止。
有关速率限制的详细信息,请参阅 速率限制。
若要了解如何设计高效的 OData 查询,请参阅本文后面的 性能指南 。
✔️ 如果查询因超时而失败,请等待或停止操作
与超出使用限制类似,如果查询遇到超时,应等待或停止操作。 它可能会发出暂时性问题的信号,因此可以重试一次以查看问题是否得到解决。 但是,持续超时表明查询可能太昂贵,无法运行。 进一步的重试只会导致超出使用限制,你将被阻止。
TF400733:请求已取消:请求已超过请求超时,请重试。
超时指示查询需要优化。 若要了解如何设计高效的 OData 查询,请参阅本文后面的 性能指南 。
❌ [已阻止]请勿将快照实体用于聚合以外的任何内容
带有 Snapshot
后缀的快照实体集很特殊,因为它们被建模为 每日快照。 可以使用它们来获取实体的状态,就像过去每天结束时一样。 例如,如果查询 WorkItemSnapshot
并筛选为单个 WorkItemId
,则创建工作项后,每天都会获得一条记录。 直接加载所有这些数据的成本很高,并且很可能超出使用限制并被阻止。 但是,允许和推荐对这些实体进行聚合。 事实上,快照实体集的设计考虑到了聚合方案。
例如,下面的查询按日期获取工作项的数量,以观察其 2020 年 1 月的增长方式。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemSnapshot?
$apply=
filter(DateSK ge 20200101 and DateSK le 20200131)/
groupby((DateSK), aggregate($count as Count))
若要了解有关聚合的详细信息,请参阅 聚合数据。
✔️ 在快照表上聚合时,请务必在 DateSK
子句中包含 groupby
或 DateValue
列
由于所有快照实体都建模为 每日快照表,因此应始终在分组子句中包含 (DateSK
或 DateValue
) 的一天属性。 否则,结果可能显示错误地膨胀。
例如,如果仅按AssignedTo
属性分组WorkItemSnapshot
并使用计数对其进行聚合,则分配给人员的所有工作项数将乘以每个工作分配处于活动状态的天数。 虽然你可能有想要的结果,但这种情况很少见。
❌ [已阻止]请勿在资源路径中使用实体键进行实体寻址
OData 语法提供了一种访问特定实体的方法,方法是将特定实体的键直接包含在 URL 段中。 有关详细信息,请参阅 OData 版本 4.0。第 2 部分:URL 约定 - 4.3 寻址实体。 尽管 OData 允许此类寻址,但 Analytics 会阻止它。 包含在查询中会导致以下错误。
URI 中指定的查询无效。 Analytics 不支持键或属性导航,例如 WorkItems (Id) 或 WorkItem (Id) /AssignedTo。 如果在 PowerBI 中遇到该错误,请重写查询,以避免导致 N+1 问题的错误折叠。
正如错误消息所提示的,某些客户端工具可能会滥用直接实体寻址。 此类客户端可以选择单独查询每个实体,而不是在单个请求中加载所有数据。 不建议这种做法,因为它可能会导致大量请求。 相反,我们建议使用显式实体寻址,如以下部分所述。
✔️ 使用筛选器子句显式寻址实体
如果要提取单个实体的数据,应使用与实体集合相同的方法,并在 子句中 $filter
显式定义筛选器。
例如,以下查询按标识符获取单个工作项。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=WorkItemId eq {id}
&$select=WorkItemId, Title
如果不确定应在此类筛选器中包含哪些属性,可以在元数据中查找它。 请参阅 构造用于 Analytics 的 OData 查询,URL 组件以查询元数据。 属性位于 的 元素中Key
EntityType
。 例如, WorkItemId
和 Revision
是实体的 WorkItemRevision
键列。
<EntityType Name="WorkItemRevision">
<PropertyRef Name="WorkItemId"/>
<PropertyRef Name="Revision"/>
[...]
</EntityType>
❌[已阻止]不要在实体上WorkItem
展开Revisions
分析数据模型不允许某些类型的扩展。 其中一个可能令某些人吃惊的是实体 Revisions
上的 WorkItem
集合属性。 如果尝试展开此属性,将收到以下错误消息。
URI 中指定的查询无效。 属性“Revisions”不能在$expand查询选项中使用。
此限制旨在鼓励每个人使用建议的解决方案,该解决方案从 WorkItemRevisions
中提取修订,如下一部分所述。
✔️ WorkItemRevisions
使用实体集加载给定工作项的所有修订
每次要提取工作项或工作项集合的完整历史记录时,请使用 WorkItemRevisions
。
例如,以下查询返回具有 {id}
标识符的工作项的所有修订。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemRevisions?
$filter=WorkItemId eq {id}
&$select=WorkItemId, Title
如果你关心与特定条件匹配的所有工作项的完整历史记录,请使用导航属性上的 WorkItem
筛选器来表达该历史记录。 例如,以下查询获取所有当前活动工作项的所有修订。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemRevisions?
$filter=WorkItem/State eq 'Active'
&$select=WorkItemId, Title
❌ [已阻止]不要对不同列进行分组
使用分组操作来减少记录数。 在 子句中使用 groupby
非重复列表示存在问题,查询将立即失败。 如果意外遇到这种情况,将收到以下错误消息。
不建议使用此查询的 groupby 子句中指定的一个或多个列。
若要解决此问题,请从 groupby
子句中删除不同的列。
❌ [已阻止]不使用 countdistinct
聚合
即使 OData 支持, countdistinct
Analytics 也不支持 函数。 虽然我们计划在将来添加支持,但目前不可用。 包含此函数的查询将返回以下错误消息。
不支持使用聚合应用计数不同的查询。
❌ 避免可能导致算术溢出的聚合
在极少数情况下,聚合查询可能会遇到算术溢出问题。 例如,当你对不用于求和的某些数值属性求和时,例如 StackRank
在工作项实体中,可能会发生这种情况。 由于 适用于数据聚合的 OData 扩展 标准不提供将属性转换为其他类型的方法,因此解决此问题的唯一方法是从聚合中删除有问题的属性。
✔️ 对长查询使用批处理终结点
长查询可能会导致问题。 特别是,在以下情况下可能会出现问题:
查询具有多个自定义字段的项目。
查询以编程方式构造。
使用 HTTP GET
发送的 OData 查询的当前限制为 3,000 个字符。 如果超过此值,将返回“404 未找到”响应。
HTTP/1.1 404 Not Found
Content-Length: 0
若要解决此问题,请使用 OData 批处理终结点,如规范 OData 版本 4.0 中所述。第 1 部分:协议 - 11.7 批处理请求。 Batch 功能主要用于将多个操作分组到单个 HTTP
请求有效负载中,但你也可以将其用作查询长度限制的解决方法。 通过发送 HTTP POST
请求,可以传递任意长度的查询,服务会正确解释该查询。
❌ [已阻止]不要使用批处理终结点发送多个查询
我们将批处理终结点的使用限制为处理一批多个请求。 单个请求仍只能有一个查询。 如果尝试发送一批多个查询,操作将失败并显示以下错误消息。 唯一的解决方案是将查询拆分为多个请求。
分析不支持处理当前批处理消息包含的多个操作。 Analytics 使用 OData 批处理来支持 POST 请求,但需要将操作限制为单个请求。
❌ [已阻止]不要使用导致超过 800 列的查询
我们限制导致超过 800 列的查询。 如果对查询返回的列的选择性不够,可能会收到以下错误消息。
VS403670:指定的查询返回高于允许的 800 列限制的“N”列。 请使用显式$select (包括$expand) 选项中来限制列数。
向查询添加 $select 子句,并在查询中添加$expand操作,以避免超出此限制。
❌ 避免创建长查询
建议在构造长查询时评估方法。 尽管有许多方案需要较长的查询 (例如,复杂筛选器或) 长的属性列表,但它们通常提供设计欠佳的早期指示器。
例如,如果查询在查询 (包含许多实体键( WorkItemId eq {id 1} or WorkItemId eq {id 2} or ...
例如,) ),则可以重写它。 尝试定义一些将选择同一组实体的其他条件,而不是传递标识符。 有时可能需要修改过程 (例如,) 添加新字段或标记,但通常值得。 使用更多抽象筛选器的查询更易于维护,并且具有更大的潜力,可以更好地工作。
另一种倾向于生成长查询的方案是,例如,在包含多个单独的日期 (时, DateSK eq {dateSK 1} or DateSK eq {dateSK 2} or ...
) 。 查找另一种可用于创建更抽象的筛选器的模式。 例如,以下查询返回星期一创建的所有工作项。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedOn/DayOfWeek eq 2
&$select=WorkItemId, Title, State
✔️ 在筛选日期列时,请指定时区
时区 (Edm.DateTimeOffset
) 公开所有日期和时间信息,其偏移量与 组织的时区设置相匹配。 此数据精确且易于解释。 另一个不明显的后果是,所有筛选器都必须传递时区信息。 如果跳过它,将收到以下错误消息。
URI 中指定的查询无效。 未指定日期时间偏移量。 请使用以下任一格式 YYYY-MM-ddZ 指定自午夜以来的所有内容,或使用 yyyy-MM-ddThh:mm-hh:mm (ISO 8601 标准表示的日期和时间) 指定偏移量。
若要解决此问题,请添加时区信息。 例如,假设将组织配置为以“ (UTC-08:00) 太平洋时间 (美国 & 加拿大) ”时区显示数据,则以下查询获取自 2020 年初以来创建的所有工作项。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedDate ge 2020-01-01T00:00:00-08:00
&$select=WorkItemId, Title, State
相同的解决方案适用于具有正偏移量的时区,但是,加号字符 (+
) 在 URI 中具有特殊含义,必须正确处理它。 如果以字符) 指定 2020-01-01T00:00:00+08:00
(+
作为起点,则会收到以下错误。
URI 中指定的查询无效。 “CreatedDate ge 2020-01-01T0000 08:00”中位置 31 处的语法错误。
若要解决此问题,请将 +
字符替换为其编码版本 %2B
。 例如,假设将组织配置为以“ (UTC+08:00) 北京、重庆、香港、乌鲁木齐”时区显示数据,则以下查询返回自 2020 年初以来创建的所有工作项。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedDate ge 2020-01-01T00:00:00%2B08:00
&$select=WorkItemId, Title, State
另一种方法是使用日期代理键属性,因为它们不保留时区信息。 例如,以下查询返回自 2020 年初以来创建的所有工作项,而不考虑组织的设置。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedDateSK ge 20200101
&$select=WorkItemId, Title, State
✔️ 请衡量实现性能指南的效果
✔️ 使用聚合扩展
✔️ 在 子句中 $select
指定列
✔️ 在 子句内的 $select
expand 选项中 $expand
指定列
✔️ 在查询历史工作项数据 (WorkItemRevisions
或WorkItemSnapshot
实体集时,请定义筛选器RevisedDateSK
)
✔️ 对于跨较长时间段的趋势查询,请使用每周或每月快照
✔️ 按标记筛选时,请对工作项使用 Tags
集合属性
✔️ TagNames
如果要将工作项上的所有标记显示为文本,请使用 属性
✔️ 使用服务器驱动的分页
✔️ $top
使用查询选项限制记录数
❌ 请勿使用 tolower
和 toupper
函数执行不区分大小写的比较
❌ 不要将无限制的扩展与 $levels=max
❌ 请勿使用 $top
和 $skip
查询选项来实现客户端驱动的分页
✔️ 考虑编写查询以返回少量记录
✔️ 考虑将所选属性的数量限制为最小值
✔️ 考虑筛选日期代理项键属性 (DateSK
后缀)
✔️ 考虑对代理项键列进行筛选
✔️ 考虑在 vsts.analytics.maxsize
标头中传递首选项
❌避免在 或 子句中使用 Parent
$filter
、 Children
或 $expand
属性Revisions
与任何性能建议一样,不应盲目实现它们。 相反,请始终捕获基线并 衡量 所做更改的效果。 所有准则都是基于与具有特定要求和挑战的 Analytics 客户端的交互创建的。 这些建议被视为一般建议,对于设计类似查询的任何人都可能有用。 但是,在极少数情况下,遵循准则不会对性能产生任何影响,甚至会产生负面影响。 确实需要测量差异才能注意到它。 如果发生这种情况,请在开发者社区门户中提供反馈。
有许多选项可用于衡量性能。 最简单的方法就是直接在浏览器中运行同一查询的两个版本。 观察开发人员工具所需的时间。 例如,可以在 Microsoft Edge F12 开发人员工具) 中使用网络面板。 另一个选项是使用 Fiddler Web 调试器工具捕获此信息。
无论采用哪种方法,都多次运行这两个查询。 例如,每个查询运行 30 次,以获取足够大的示例集。 然后找出性能特征。 分析遵循多租户体系结构。 因此,查询的持续时间可能会受到同时发生的其他操作的影响。
✔️ 使用聚合扩展
到目前为止,提高查询性能的最佳方法是使用聚合扩展 - OData 扩展进行数据聚合。 使用聚合扩展,要求服务汇总服务器端的数据,并返回比应用相同函数客户端可以提取的响应小得多的响应。 最后,Analytics 针对此类查询进行优化,因此请利用它。
若要了解详细信息,请参阅 聚合数据。
✔️ 在 子句中 $select
指定列
在 子句中指定你关注的 $select
列。 分析基于 列存储索引 技术构建。 这意味着数据既是存储,也是基于列的查询处理。 通过减少属性集,引用 in $select
子句可以减少必须扫描的列数并提高查询的整体性能。
例如,以下查询指定工作项的列。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$select=WorkItemId, Title, State
Azure DevOps 支持流程自定义。 某些管理员使用此功能并创建数百个自定义字段。 如果省略 $select
子句,查询将返回所有字段,包括自定义字段。
✔️ 在 子句内的 $select
expand 选项中 $expand
指定列
与 $select
子句准则类似,请在 子句的 $select
expand 选项中 $expand
指定属性。 很容易忘记,但如果省略它,响应将包含展开对象中的所有属性。
例如,下面的查询指定工作项及其父项的列。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$select=WorkItemId, Title, State
&$expand=Parent($select=WorkItemId, Title, State)
✔️ 在查询历史工作项数据 (WorkItemRevisions
或WorkItemSnapshot
实体集时,请定义筛选器RevisedDateSK
)
查询历史数据时,你可能对最近一段时间 (感兴趣,例如 30 天、90 天) 。 由于工作项实体的实现方式,可以通过一种方便的方式编写此类查询,以获得出色的性能。 每次更新工作项时,它都会创建一个新修订,并在字段中记录此操作 System.RevisedDate
,因此非常适合历史记录筛选器。
在 Analytics 中,修订日期由 RevisedDate
() Edm.DateTimeOffset
和 RevisedDateSK
(Edm.Int32
) 属性表示。 为了获得最佳性能,请使用后者。 它是日期 代理键 ,它表示创建修订的日期,或者它对于 null
活动、不完整的修订具有的日期。 如果需要自非独占日期以来 {startDate}
的所有日期,请将以下筛选器添加到查询。
RevisedDateSK eq null or RevisedDateSK gt {startDateSK}
例如,以下查询返回自 2020 年初以来每天的工作项数。 请注意,除了列的 DateSK
明显筛选器外,还有对 RevisedDateSK
的第二个筛选器。 尽管它看起来可能多余,但它可帮助查询引擎筛选出不在范围内的修订,并显著提高查询性能。
https://analytics.dev.azure.com/{OrganizationName}/_odata/v1.0/WorkItemSnapshot?
$apply=
filter(DateSK gt 20200101)/
filter(RevisedDateSK eq null or RevisedDateSK gt 20200101)/
groupby(
(DateValue),
aggregate($count as Count)
我们在处理燃尽小组件时提出了此建议。 最初,我们仅为 定义 DateSK
筛选器,但无法使此查询适合具有大型数据集的组织。 在查询分析期间,我们注意到 DateSK
无法很好地筛选修订。 只有在添加筛选器 RevisedDateSK
后,我们才能获得出色的大规模性能。
~ 产品团队
✔️ 对于跨较长时间段的趋势查询,请使用每周或每月快照
默认情况下,所有快照表都建模为 每日快照事实 表。 如果查询某个时间范围,它将获取每天的值。 较长的时间范围会导致大量记录。 如果不需要如此高的精度,可以使用每周甚至每月快照。
可以使用其他筛选表达式执行此操作,以删除未完成给定周或月的天数。 IsLastDayOfPeriod
使用 已添加到 Analytics 的 属性,考虑到此方案。 此属性的类型为 Microsoft.VisualStudio.Services.Analytics.Model.Period
,可以确定一天是否在不同的时间段 (完成,例如,周、月等) 。
<EnumType Name="Period" IsFlags="true">
<Member Name="None" Value="0"/>
<Member Name="Day" Value="1"/>
<Member Name="WeekEndingOnSunday" Value="2"/>
<Member Name="WeekEndingOnMonday" Value="4"/>
<Member Name="WeekEndingOnTuesday" Value="8"/>
<Member Name="WeekEndingOnWednesday" Value="16"/>
<Member Name="WeekEndingOnThursday" Value="32"/>
<Member Name="WeekEndingOnFriday" Value="64"/>
<Member Name="WeekEndingOnSaturday" Value="128"/>
<Member Name="Month" Value="256"/>
<Member Name="Quarter" Value="512"/>
<Member Name="Year" Value="1024"/>
<Member Name="All" Value="2047"/>
</EnumType>
由于 Microsoft.VisualStudio.Services.Analytics.Model.Period
定义为具有标志的枚举,因此请使用 OData has
运算符并为句点文本指定完整类型。
IsLastDayOfPeriod has Microsoft.VisualStudio.Services.Analytics.Model.Period'Month'
例如,以下查询返回在每个月的最后一天定义的工作项计数。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemSnapshot?
$apply=
filter(IsLastDayOfPeriod has Microsoft.VisualStudio.Services.Analytics.Model.Period'Month')/
groupby(
(DateValue),
aggregate($count as Count)
可以将 TagNames
属性与 函数一起使用 contains
,以确定工作是否已使用特定标记进行标记。 但是,此方法可能会导致查询速度缓慢,尤其是在同时检查多个标记时。 为了获得最佳性能和结果,请改用 Tags
导航属性。
例如,以下查询获取使用 {tag}
标记的所有工作项。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=Tags/any(t:t/TagName eq '{tag}')
&$select=WorkItemId, Title, State
如果需要筛选多个标记,此方法也非常有效。 例如,以下查询返回使用 或 标记{tag1}
的所有工作项{tag2}
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=Tags/any(t:t/TagName eq {tag1} or t/TagName eq {tag2})
&$select=WorkItemId, Title, State
还可以将这些筛选器与“and”运算符组合在一起。 例如,以下查询获取使用 和 标记{tag1}
的所有工作项{tag2}
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=Tags/any(t:t/TagName eq {tag1}) and Tags/any(t:t/TagName eq {tag2})
&$select=WorkItemId, Title, State
✔️ TagNames
如果要将工作项上的所有标记显示为文本,请使用 属性
上一部分所述的导航属性 Tags
非常适合筛选。 但是,使用它们会带来一些挑战,因为查询返回嵌套集合中的标记。 数据模型还包含基 TagNames
元属性 (Edm.String
) ,我们添加了该属性以简化标记使用方案。 它是一个文本值,其中包含所有标记的列表,并带有分号“; ”分隔符。 当你关心的只是一起显示标记时,请使用此属性。 可以将其与前面所述的标记筛选器组合在一起。
例如,以下查询获取使用 {tag}
标记的所有工作项。 它返回工作项 ID、标题、状态和组合标记的文本表示形式。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=Tags/any(t:t/TagName eq '{tag}')
&$select=WorkItemId, Title, State, TagNames
属性 TagNames
的长度限制为 1024 个字符。 它包含一组符合该限制的标记。 如果工作项有许多标记或标记很长,则 TagNames
不会包含完整集,应 Tag
改用导航属性。
❌ 不要使用 tolower
和 toupper
函数进行不区分大小写的比较
如果已使用过其他系统,则可能需要使用 tolower
或 toupper
函数进行不区分大小写的比较。 使用 Analytics 时,所有字符串比较默认不区分大小写,因此无需应用任何函数来显式处理它。
例如,以下查询获取标记有“QUALITY”、“quality”或此单词的任何其他大小写组合的所有工作项。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=Tags/any(t:t/TagName eq 'quality')
&$select=WorkItemId, Title, State, TagNames
❌ 不要对 使用无限扩展 $levels=max
OData 能够扩展分层结构的所有级别。 例如,工作项跟踪具有一些可以应用无限扩展的实体。 此操作仅适用于具有少量数据的组织。 对于较大的数据集,它无法很好地缩放。 在以下的情况下,完全不要使用它:
你正在使用大型数据集。
你正在开发小组件,并且无法控制小组件的安装位置。
✔️ 使用服务器驱动的分页
如果请求的集太大而无法在单个响应中发送,Analytics 将应用分页。 响应将仅包含一个部分集和一个允许检索下一部分项集的链接。 OData 规范 - OData 版本 4.0 中介绍了此策略。第 1 部分:协议 - Server-Driven分页。 通过让服务控制分页,你可以获得最佳性能, skiptoken
因为 经过精心设计,每个实体都尽可能高效。
指向下一页的链接包含在 属性中 @odata.nextLink
。
"@odata.context": "https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/$metadata#WorkItems(*)",
"value": [
"@odata.nextLink":"https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?$skiptoken=12345"}
大多数现有的 OData 客户端都可以自动处理服务器驱动的分页。 例如,以下工具已使用此策略:Power BI、SQL Server Integration Services 和 Azure 数据工厂。
❌ 不要使用 $top
和 $skip
查询选项来实现客户端驱动的分页
对于其他 REST API,你可能已使用 和 $skip
查询选项实现了客户端驱动的分页$top
。 不要将它们与 Analytics 一起使用。 此方法存在几个问题,性能就是其中之一。 相反,请采用上一部分所述的服务器驱动的分页策略。
✔️ $top
请使用查询选项来限制记录数
仅当与 $skip
一起使用时,才不建议使用查询选项$top
。 如果在报告方案中,只需一部分记录 (例如示例) ,则可以使用 $top
查询选项。 此外,如果需要根据某些条件对记录进行排名,应始终结合使用 $top
来 $orderby
获得排名靠前的记录的稳定结果。
✔️ 考虑编写查询以返回少量记录
编写查询以返回少量记录是最直观的准则。 始终旨在仅提取你真正关心的数据。 可以通过在 OData 查询语言中提供大部分强大的筛选功能来实现此目的。
✔️ 考虑将所选属性的数量限制为最小值
某些项目管理员通过添加自定义字段来大量自定义其流程。 在提取宽实体上的所有可用列(例如) )时, WorkItems
大量自定义可能会导致性能问题 (。 分析基于 列存储索引 技术构建。 这意味着数据既是存储的,也是基于列的查询处理。 因此,查询引用的属性越多,处理成本就越高。 始终旨在将查询中的属性集限制为在报告方案中真正关心的属性。
✔️ 考虑筛选日期代理键属性 (DateSK
后缀)
有多种方法可以定义日期筛选器。 可以直接 (日期属性进行筛选,例如, CreatedDate
) 、其导航对应项 (例如 CreatedOnDate
,) 或其代理键表示形式 (CreatedDate
例如) 。 最后一个选项会产生最佳性能,在报告要求允许时是首选选项。
例如,以下查询获取自 2020 年初以来创建的所有工作项。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedDateSK ge 20200101
✔️ 考虑对代理键列进行筛选
例如,如果要根据相关对象的值筛选数据, (筛选项目名称上的工作项) ,则始终有两种选择。 可以使用导航属性 (例如 Project/ProjectName
,) 或预先捕获 代理键 ,并直接在查询 (使用它, ProjectSK
例如,) 。
如果要生成小组件,建议使用后一个选项。 当密钥作为查询的一部分传递时,必须触摸的实体集数量会减少,性能也会提高。
例如,以下查询使用 ProjectSK
属性而不是Project/ProjectName
导航属性筛选WorkItems
。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=ProjectSK eq {projectSK}
❌避免在 $filter
或 子句中使用 Parent
、 Children
或 $expand
Revisions
属性
工作项是整个数据模型中成本最高的实体。 它们具有多个可用于访问相关工作项的导航属性:Parent
、、Children
Revisions
。 但是,每次在查询中使用它们时,性能都会下降。 始终会质疑你是否真的需要其中一个属性,并可能更新设计。
例如,可以提取更多工作项并使用 ParentWorkItemId
属性重新构造整个层次结构客户端,而不是展开 Parent
。 根据具体情况执行此类优化。
执行查询时,不知道查询将返回的记录数。 必须发送另一个包含聚合的查询,或者遵循所有后续链接并提取整个数据集。 分析遵循 VSTS.Analytics.MaxSize
首选项,这允许在数据集大于客户端可以接受的实例中快速失败。
此选项在数据导出方案中很有用。 若要使用它,必须向 HTTP 请求添加 Prefer
标头,并将其设置为 VSTS.Analytics.MaxSize
非负值。 该值 VSTS.Analytics.MaxSize
表示可以接受的最大记录数。 如果将其设置为零,则将使用默认值 200K。
例如,如果数据集较小或等于 1000 条记录,则以下查询将返回工作项。
GET https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems HTTP/1.1
User-Agent: {application}
Prefer: VSTS.Analytics.MaxSize=1000
OData-MaxVersion: 4.0
Accept: application/json;odata.metadata=minimal
Host: analytics.dev.azure.com/{OrganizationName}
如果数据集超过 1000 条记录的限制,则查询将立即失败并出现以下错误。
查询结果包含 1,296 行,超过允许的最大大小 1000。 请通过应用其他筛选器减少记录数
查询样式指南
✔️ $count
在聚合方法中使用虚拟属性
❌ 避免在 URL 段中使用 $count
虚拟属性
❌ 避免在单个查询中混合 $apply
和 $filter
子句
✔️ 考虑使用参数别名分隔查询的可变部分
✔️ 考虑构建查询以匹配 OData 评估顺序
✔️ 考虑查看元数据注释中所述的 OData 功能
✔️ $count
在聚合方法中使用虚拟属性
某些实体公开 Count
属性。 当数据导出到其他存储时,它们使某些报告方案更容易。 但是,不应在 OData 查询的聚合中使用这些列。 $count
请改用虚拟属性。
例如,以下查询返回工作项的总数。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$apply=aggregate($count as Count)
❌ 避免在 URL 段中使用 $count
虚拟属性
尽管 OData 标准允许将 $count
虚拟属性用于实体集 (例如 _odata/v1.0/WorkItems/$count
) ,但并非所有客户端都可以正确解释响应。 因此,建议改用聚合。
例如,以下查询返回工作项的总数。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$apply=aggregate($count as Count)
✔️ 考虑使用参数别名分隔查询的可变部分
参数别名提供了一种优雅的解决方案,用于从主查询文本中提取参数值等可变部分。 可以在计算结果的表达式中使用它们:
基元值或复杂值的集合。
有关详细信息,请参阅 OData 版本 4.0。第 2 部分:URL 约定 - 5.1.1.13 参数别名。 当查询文本用作可以使用用户提供的值实例化的模板时,参数非常有用。
例如,以下查询使用 @createdDateSK
参数将值与筛选器表达式分开。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=CreatedDateSK ge @createdDateSK
&$select=WorkItemId, Title, State
&@createdDateSK=20200101
❌ 避免在单个查询中混合 $apply
和 $filter
子句
如果要将 添加到 filter
查询,有两个选项。 可以使用 子句或 $apply=filter()
组合执行此操作$filter
。 这些选项中的每一个本身都效果很好,但将它们组合在一起可能会导致一些意外的结果。
尽管有预期,OData 清楚地定义了评估顺序。 此外, $apply
子句优先于 $filter
。 出于此原因,应选择一个或另一个,但避免在单个查询中选择这两个筛选器选项。 如果查询是自动生成的,这一点很重要。
例如,以下查询首先按 StoryPoint gt 5
筛选工作项,按 路径聚合结果,最后按 筛选 StoryPoints gt 2
结果。 使用此求值顺序,查询将始终返回空集。
https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
$filter=StoryPoints gt 2
$apply=
filter(StoryPoints gt 5)/
groupby(
(Area/AreaPath),
aggregate(StoryPoints with sum as StoryPoints)
✔️ 考虑构建查询以匹配 OData 评估顺序
由于在单个查询中混合 $apply
和 filter
子句可能会导致混淆,因此建议构建查询子句以匹配计算顺序。
$apply
$filter
$orderby
$expand
$select
$skip
如果不确定 Analytics 支持的 OData 功能,可以在元数据中查找批注。 TC GitHub 存储库中的 OASIS 开放数据协议 (OData) 技术委员会维护可用批注的列表。
例如,支持筛选器函数的列表在实体容器上的注释中 Org.OData.Capabilities.V1.FilterFunctions
可用。
<Annotation Term="Org.OData.Capabilities.V1.FilterFunctions">
<Collection>
<String>contains</String>
<String>endswith</String>
[...]
</Collection>
</Annotation>
另一个有用的注释是 Org.OData.Capabilities.V1.ExpandRestrictions
,它解释了哪些导航属性不能在 子句中使用 $expand
。 例如,以下批注说明Revisions
WorkItems
实体集中无法展开。
<EntitySet Name="WorkItems" EntityType="Microsoft.VisualStudio.Services.Analytics.Model.WorkItem">
[...]
<Annotation Term="Org.OData.Capabilities.V1.ExpandRestrictions">
<Record>
<PropertyValue Property="Expandable" Bool="true"/>
<PropertyValue Property="NonExpandableProperties">
<Collection>
<NavigationPropertyPath>Revisions</NavigationPropertyPath>
</Collection>
</PropertyValue>
</Record>
</Annotation>
</EntitySet>
构造用于分析的 OData 查询
查询工作项跟踪数据
查询趋势数据
查询工作项链接
支持的函数 & 子句
工作跟踪、进程和项目限制