src/Query/Get-XrmRecordCount.ps1
|
<# .SYNOPSIS Count the rows matching a query. .DESCRIPTION Return the exact number of rows matching a QueryExpression or a FetchXml query, filters and links included. The count is first asked as an aggregate (countcolumn, distinct on the primary key, so a one-to-many link does not count a row twice). When the platform refuses the aggregate (more than 50,000 rows: AggregateQueryRecordLimit), the rows are paged, reading only their ids. Columns and orders of the query are ignored; a top / TopCount caps the result. The caller's query is not modified. For approximate counts of whole tables, see Get-XrmTotalRecordCount. .PARAMETER XrmClient Xrm connector initialized to target instance. Use latest one by default. (Dataverse ServiceClient) .PARAMETER Query QueryExpression selecting the rows to count. .PARAMETER FetchXml FetchXml query selecting the rows to count. .PARAMETER NoAggregate Skip the aggregate and count by paging (for tables that refuse aggregates). .OUTPUTS System.Int64. Number of matching rows. .EXAMPLE $query = New-XrmQueryExpression -LogicalName "account" | Add-XrmQueryCondition -Field "statecode" -Condition Equal -Values 0; $activeAccounts = Get-XrmRecordCount -XrmClient $xrmClient -Query $query; .EXAMPLE $count = Get-XrmRecordCount -XrmClient $xrmClient -FetchXml '<fetch><entity name="contact"><filter><condition attribute="parentcustomerid" operator="not-null" /></filter></entity></fetch>'; .LINK https://github.com/AymericM78/PowerDataOps/blob/main/documentation/commands/Get-XrmRecordCount.md #> function Get-XrmRecordCount { [CmdletBinding(DefaultParameterSetName = "Query")] [OutputType([long])] param ( [Parameter(Mandatory = $false, ValueFromPipeline)] [Microsoft.PowerPlatform.Dataverse.Client.ServiceClient] $XrmClient = $Global:XrmClient, [Parameter(Mandatory = $true, ParameterSetName = "Query")] [ValidateNotNull()] [Microsoft.Xrm.Sdk.Query.QueryExpression] $Query, [Parameter(Mandatory = $true, ParameterSetName = "FetchXml")] [ValidateNotNullOrEmpty()] [String] $FetchXml, [Parameter(Mandatory = $false)] [switch] $NoAggregate ) begin { $StopWatch = [System.Diagnostics.Stopwatch]::StartNew(); Trace-XrmFunction -Name $MyInvocation.MyCommand.Name -Stage Start -Parameters ($MyInvocation.MyCommand.Parameters); } process { if ($PSCmdlet.ParameterSetName -eq "Query") { $conversionRequest = New-XrmRequest -Name "QueryExpressionToFetchXml"; $conversionRequest | Add-XrmRequestParameter -Name "Query" -Value $Query | Out-Null; $FetchXml = ($XrmClient | Invoke-XrmRequest -Request $conversionRequest).Results["FetchXml"]; } # Strip the query down to its filters and links [xml]$fetchDocument = $FetchXml; $fetchNode = $fetchDocument.DocumentElement; # Only top / TopCount caps the count: "count" and "page" come from paging (e.g. a query already run by Get-XrmMultipleRecords) $top = $null; if ($PSCmdlet.ParameterSetName -eq "Query") { $top = $Query.TopCount; } elseif ($fetchNode.HasAttribute("top")) { $top = [long]$fetchNode.GetAttribute("top"); } foreach ($attributeName in @("top", "count", "page", "paging-cookie", "aggregate", "distinct", "returntotalrecordcount")) { $fetchNode.RemoveAttribute($attributeName); } foreach ($node in @($fetchDocument.SelectNodes("//attribute | //all-attributes | //order"))) { $node.ParentNode.RemoveChild($node) | Out-Null; } $entityNode = $fetchDocument.SelectSingleNode("/fetch/entity"); $logicalName = $entityNode.GetAttribute("name"); $primaryIdAttribute = ($XrmClient | Get-XrmEntityMetadata -LogicalName $logicalName -Filter ([Microsoft.Xrm.Sdk.Metadata.EntityFilters]::Entity)).PrimaryIdAttribute; $count = $null; if (-not $NoAggregate) { $aggregateDocument = $fetchDocument.Clone(); $aggregateDocument.DocumentElement.SetAttribute("aggregate", "true"); $countNode = $aggregateDocument.CreateElement("attribute"); $countNode.SetAttribute("name", $primaryIdAttribute); $countNode.SetAttribute("alias", "pdorecordcount"); $countNode.SetAttribute("aggregate", "countcolumn"); $countNode.SetAttribute("distinct", "true"); $aggregateEntityNode = $aggregateDocument.SelectSingleNode("/fetch/entity"); $aggregateEntityNode.InsertBefore($countNode, $aggregateEntityNode.FirstChild) | Out-Null; try { $fetchExpression = [Microsoft.Xrm.Sdk.Query.FetchExpression]::new($aggregateDocument.OuterXml); $result = Protect-XrmCommand -ScriptBlock { $XrmClient.RetrieveMultiple($fetchExpression) }; $count = [long]$result.Entities[0]["pdorecordcount"].Value; } catch { # Aggregates are refused above 50,000 rows: count by paging instead if ($_.Exception.Message -notmatch "AggregateQueryRecordLimit") { throw; } } } if ($null -eq $count) { $pagedQuery = $XrmClient | Get-XrmQueryFromFetch -FetchXml $fetchDocument.OuterXml; $pagedQuery.ColumnSet = [Microsoft.Xrm.Sdk.Query.ColumnSet]::new($false); $pagedQuery.Distinct = $true; $pagedQuery.TopCount = $null; $counter = @{ Value = [long]0 }; Invoke-XrmQueryPagesInternal -XrmClient $XrmClient -Query $pagedQuery -PageSize 5000 -OnPage { param($page) $counter.Value += $page.Entities.Count; }; $count = $counter.Value; } if ($null -ne $top -and $top -lt $count) { $count = $top; } $count; } end { $StopWatch.Stop(); Trace-XrmFunction -Name $MyInvocation.MyCommand.Name -Stage Stop -StopWatch $StopWatch; } } Export-ModuleMember -Function Get-XrmRecordCount -Alias *; |