Skip to content

Query Checks

Configure one Query Check that turns SOQL results into a count, a single value, a decision across several rows, or a list-membership check.

Reference

  • This page defines the required fields, query-result choices, Expected Value choices, access behavior, limits, and outcomes.
  • For every field’s size, default, help text, and examples, use the Check field reference.

Zero returned rows means the running user could not see a matching row. It does not prove the org contains no matching data. Test the Check as a representative non-administrator user.

Setup fieldAPI nameRequirement
Evaluation TypeEvaluationType__cVerify with a query: QUERY
Source QuerySourceQuery__cPrimary SOQL template; required except list-membership mode
Source Query FieldSourceQueryField__cSelected field or aggregate alias; blank for bare COUNT()
How To Read Query ResultsQueryResultHandling__cConverts returned rows into the value or row decision
Comparison OperatorComparisonOperator__cRequired operator compatible with the selected mode
Expected Value Comes FromExpectedValueSource__cRequired when the operator needs a right-side value
Expected Currency ISO CodeExpectedCurrencyIsoCode__cRequired in a multi-currency org when a Currency field is compared with a fixed value
Setup labelAPI valueBehavior
One row or aggregateONE_RESULTCompare one selected field, COUNT(), or aliased aggregate
Any record passesANY_ROW_PASSESPASS when at least one returned row satisfies the comparison
Every record passesALL_ROWS_PASSPASS only when every evaluated row satisfies the comparison
Compare as listsCOMPARE_AS_LISTSUse a supported membership operator and explicit no-row behavior

For row modes, Source Query Field identifies the compared column. For a bare COUNT(), leave the field blank. For SUM(), AVG(), MIN(), or MAX(), give the aggregate an alias and enter that alias as Source Query Field.

To return PASS when an Account has at least one Contact:

SELECT COUNT()
FROM Contact
WHERE AccountId = {!record.Id}

Use One row or aggregate, Greater than or equal, Fixed value, and an Expected Value of 1. Leave Source Query Field blank because bare COUNT() does not use an alias.

To confirm that every open Opportunity has a Next Step:

SELECT NextStep
FROM Opportunity
WHERE AccountId = {!record.Id} AND IsClosed = FALSE

Set Source Query Field to NextStep, choose Every record passes, and use Is not empty. Then decide explicitly what should happen when the Account has no open Opportunities by setting If Query Finds No Records.

Setup labelAPI valueAdditional field
Fixed valueFIXED_VALUEExpected Value (Fixed)
Record formulaRECORD_FORMULAExpected Value (Formula)
Comparison queryCOMPARISON_QUERYComparison Query and, when needed, Comparison Query Field

Leave Expected Value Comes From blank for Is empty and Is not empty. Compare-two-queries Checks also leave it blank because the second query is inherently the right side.

Single-value and row modes support equality, ordering, contains, and empty-value operators as documented under Comparison Operator.

Query list-membership uses:

  • List contains any: LIST_CONTAINS_ANY
  • List contains none: LIST_CONTAINS_NONE

For those operators, set How To Read Query Results to Compare as lists, put the current-record value in Value to find in the list (formula), and return the candidate list from Comparison Query. Source Query is blank in this mode.

Setup fieldAPI nameBehavior
If Query Finds No RecordsNoRowsResult__cReturns Pass, Fail, Skip, or Unable to evaluate when a query returns zero rows
If Field Value Is EmptyEmptyValueHandling__cIgnore the row, compare blank, or force no match
Max Query Rows (1-2000)MaxQueryRows__cDefaults to 200; maximum 2000

No-row behavior is a business decision. Configure it explicitly where required; zero rows can mean pass, fail, skip, or unable depending on the Check.

Record Health Check probes one row beyond Max Query Rows so it never turns a truncated collection into a collection-wide verdict. When the probe returns that extra row, the Check returns UNABLE_TO_EVALUATE / ROW_LIMIT_EXCEEDED. The administrator detail names the configured cap but does not disclose the true row count. Narrow the query or raise the configured cap.

A bare COUNT() query always returns one aggregate row, even when the count is zero. If Query Finds No Records therefore does not replace a COUNT() value of zero. Compare that zero with the Expected Value normally.

If Field Value Is Empty applies when a row exists but the selected field is null. That is different from the query returning no rows:

  • Ignore the record removes that row from a multi-row decision.
  • Treat as blank compares the value as empty text.
  • Treat as not matching makes that value fail to match another value, including another empty value.

Record Health Check compares values; it does not convert currencies. In a multi-currency org, non-aggregate Query and Compare two queries checks retain each returned row’s CurrencyIsoCode. When the reachable codes across both sides differ, evaluation returns UNABLE_TO_EVALUATE with MIXED_CURRENCY. A fixed value compared with a Currency field must declare its unit in Expected Currency ISO Code, and that declaration participates in the same guard.

SUM, AVG, MIN, and MAX collapse the source rows before Apex receives the result, so a corporate-currency display label is not evidence that the inputs shared a unit. Metadata validation therefore rejects an aggregate over a Currency field unless the query groups by CurrencyIsoCode or its outer WHERE clause requires CurrencyIsoCode to equal one literal ISO code. Requiring one ISO code preserves a one-row aggregate result. An OR or NOT in the outer WHERE clause means the equality might not apply to every contributing row, so Record Health Check rejects the Check. Conditions inside semi-joins do not change that decision. Grouping changes the result shape.

An alternative is a custom Apex Check that explicitly owns and carries unit semantics. Formula Checks cannot reliably inspect CurrencyIsoCode and are not covered by this guard. Single-currency orgs have no row ISO field and are unaffected.

For the supported flat and aggregate query subset, Record Health Check checks selected fields and the fields used by aggregate functions against Salesforce field definitions before execution. This preserves locale-independent FIELD_NOT_ACCESSIBLE, FIELD_NOT_RESOLVED, and relationship classifications for aliased aggregate results.

  • Use {!record.Id} and supported {!record.FieldName} tokens for current-record values. Add a fallback that matches the Salesforce field’s data type when an empty value needs a substitute, such as {!record.AnnualRevenue fallback="0"}.
  • Use complete API names for fields and relationships owned by another installed package. For example, select and enter SBQQ__AssetQuantitiesCombined__c in both Source Query and Source Query Field. Record Health Check does not add, remove, or guess namespace prefixes.
  • Use field API names, not labels, in SOQL.
  • Queries run in user mode and enforce the running user’s record, object, and field access.
  • A zero-row result means no rows were visible to that user-mode transaction. It does not establish org-wide absence, and the framework never uses elevated queries to infer hidden rows.
  • A missing object, field, record, or relationship permission can return UNABLE_TO_EVALUATE.
  • Store reviewed SOQL in Check Custom Metadata. Do not build Check SOQL from text entered by an end user.
  • Keep the selected columns and row limit as small as the decision requires.
  • Base64/Blob selected fields are refused before execution with FIELD_TYPE_NOT_SUPPORTED. When a business decision genuinely depends on binary content, use reviewed user-mode Apex and return only the redacted business outcome, never the binary value.

PASS and FAIL are completed business decisions. SKIPPED means the Check did not apply or a dependency prevented it. Configuration, access, unsupported SOQL, or value-conversion problems return UNABLE_TO_EVALUATE with a stable Reason Code; unexpected Apex failures return ERROR.

Test the pass, fail, no-row, empty-field, row-cap, access-denied, and every configured applicability or prerequisite path. For aggregate queries, also test null aggregate values and the exact alias.

The Apex response does not contain a Query-specific version number. Its global Apex types are the compile-time contract supplied by the installed package. Flow responses currently report contract 2.0, and Platform Events report their separate contract 1.0. Removing or renaming a public field, Status, operator, or Reason Code requires a new contract version. No Query field is currently deprecated.